Compare commits

...

8 Commits

Author SHA1 Message Date
Flatlogic Bot
db6c439a10 bit 2026-02-07 06:44:55 +00:00
Flatlogic Bot
48c3bc0ba6 BIT 2026-02-07 06:32:54 +00:00
Flatlogic Bot
752e63d5fc bit 2026-02-07 06:11:37 +00:00
Flatlogic Bot
f1fc7be962 BIT 2026-02-07 06:05:18 +00:00
Flatlogic Bot
0d9e8b5e50 Autosave: 20260207-055352 2026-02-07 05:53:52 +00:00
Flatlogic Bot
eb60696dd9 BIT 2026-02-06 12:45:13 +00:00
Flatlogic Bot
65d8cd957f BIT 2026-02-06 12:09:26 +00:00
Flatlogic Bot
6a5fce6db2 Autosave: 20260206-092300 2026-02-06 09:23:02 +00:00
78 changed files with 6774 additions and 328 deletions

377
admin.php Normal file
View File

@ -0,0 +1,377 @@
<?php
include_once 'config.php';
if (!isset($_SESSION['admin_id'])) {
header("Location: admin_login.php");
exit;
}
$action = $_GET['action'] ?? 'dashboard';
// Handle Post Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['update_balance'])) {
$stmt = db()->prepare("UPDATE accounts SET balance = ? WHERE id = ?");
$stmt->execute([$_POST['balance'], $_POST['account_id']]);
$msg = "余额更新成功";
}
if (isset($_POST['update_win_loss'])) {
$stmt = db()->prepare("UPDATE accounts SET win_loss_control = ? WHERE id = ?");
$stmt->execute([$_POST['win_loss_control'], $_POST['account_id']]);
$msg = "输赢控制已更新";
}
if (isset($_POST['update_kyc'])) {
$stmt = db()->prepare("UPDATE accounts SET kyc_status = ? WHERE id = ?");
$stmt->execute([$_POST['kyc_status'], $_POST['account_id']]);
$msg = "认证状态已更新";
}
if (isset($_POST['approve_deposit'])) {
db()->beginTransaction();
$stmt = db()->prepare("SELECT * FROM transactions WHERE id = ? AND status = 'pending' AND transaction_type = 'deposit'");
$stmt->execute([$_POST['transaction_id']]);
$trx = $stmt->fetch();
if ($trx) {
db()->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?")->execute([$trx['amount'], $trx['account_id']]);
db()->prepare("UPDATE transactions SET status = 'completed' WHERE id = ?")->execute([$trx['id']]);
$msg = "充值已批准";
}
db()->commit();
}
if (isset($_POST['approve_withdraw'])) {
db()->beginTransaction();
$stmt = db()->prepare("SELECT * FROM transactions WHERE id = ? AND status = 'pending' AND transaction_type = 'withdraw'");
$stmt->execute([$_POST['transaction_id']]);
$trx = $stmt->fetch();
if ($trx) {
db()->prepare("UPDATE accounts SET frozen_balance = frozen_balance - ? WHERE id = ?")->execute([$trx['amount'], $trx['account_id']]);
db()->prepare("UPDATE transactions SET status = 'completed' WHERE id = ?")->execute([$trx['id']]);
$msg = "提现已批准";
}
db()->commit();
}
if (isset($_POST['reject_transaction'])) {
db()->beginTransaction();
$stmt = db()->prepare("SELECT * FROM transactions WHERE id = ? AND status = 'pending'");
$stmt->execute([$_POST['transaction_id']]);
$trx = $stmt->fetch();
if ($trx && $trx['transaction_type'] === 'withdraw') {
// Unfreeze balance
db()->prepare("UPDATE accounts SET balance = balance + ?, frozen_balance = frozen_balance - ? WHERE id = ?")->execute([$trx['amount'], $trx['amount'], $trx['account_id']]);
}
db()->prepare("UPDATE transactions SET status = 'failed' WHERE id = ?")->execute([$_POST['transaction_id']]);
$msg = "交易已驳回";
db()->commit();
}
if (isset($_POST['update_site_settings'])) {
$stmt = db()->prepare("UPDATE site_settings SET site_name = ?, contact_email = ?, deposit_address = ? WHERE id = 1");
$stmt->execute([$_POST['site_name'], $_POST['contact_email'], $_POST['deposit_address']]);
$msg = "站点设置已更新";
}
if (isset($_POST['update_price'])) {
$stmt = db()->prepare("UPDATE cryptocurrencies SET manual_price = ? WHERE id = ?");
$stmt->execute([$_POST['manual_price'], $_POST['coin_id']]);
$msg = "价格已手动调整";
}
}
$settings = get_site_settings();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理后台 - <?php echo $settings['site_name']; ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<style>
body { background: #f4f7f6; }
.sidebar { min-height: 100vh; background: #2c3e50; color: white; }
.sidebar a { color: #bdc3c7; text-decoration: none; padding: 10px 20px; display: block; }
.sidebar a:hover, .sidebar a.active { background: #34495e; color: white; }
.card { border: none; box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- Sidebar -->
<div class="col-md-2 sidebar p-0">
<div class="p-3 text-center border-bottom border-secondary">
<h4>管理后台</h4>
</div>
<a href="?action=dashboard" class="<?php echo $action === 'dashboard' ? 'active' : ''; ?>"><i class="bi bi-speedometer2 me-2"></i> 控制台</a>
<a href="?action=users" class="<?php echo $action === 'users' ? 'active' : ''; ?>"><i class="bi bi-people me-2"></i> 用户管理</a>
<a href="?action=transactions" class="<?php echo $action === 'transactions' ? 'active' : ''; ?>"><i class="bi bi-cash-stack me-2"></i> 充值提现</a>
<a href="?action=orders" class="<?php echo $action === 'orders' ? 'active' : ''; ?>"><i class="bi bi-list-check me-2"></i> 交易记录</a>
<a href="?action=market" class="<?php echo $action === 'market' ? 'active' : ''; ?>"><i class="bi bi-graph-up me-2"></i> 市场管理</a>
<a href="?action=settings" class="<?php echo $action === 'settings' ? 'active' : ''; ?>"><i class="bi bi-gear me-2"></i> 系统设置</a>
<a href="logout.php" class="mt-5 text-danger"><i class="bi bi-box-arrow-right me-2"></i> 退出登录</a>
</div>
<!-- Main Content -->
<div class="col-md-10 p-4">
<?php if (isset($msg)): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?php echo $msg; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if ($action === 'dashboard'): ?>
<h2 class="mb-4">控制台概览</h2>
<div class="row">
<div class="col-md-3">
<div class="card bg-primary text-white p-3">
<h6>总用户</h6>
<h3><?php echo db()->query("SELECT COUNT(*) FROM accounts")->fetchColumn(); ?></h3>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white p-3">
<h6>待处理充值</h6>
<h3><?php echo db()->query("SELECT COUNT(*) FROM transactions WHERE transaction_type='deposit' AND status='pending'")->fetchColumn(); ?></h3>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-dark p-3">
<h6>待处理提现</h6>
<h3><?php echo db()->query("SELECT COUNT(*) FROM transactions WHERE transaction_type='withdraw' AND status='pending'")->fetchColumn(); ?></h3>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white p-3">
<h6>今日订单</h6>
<h3><?php echo db()->query("SELECT COUNT(*) FROM orders WHERE DATE(created_at) = CURRENT_DATE")->fetchColumn(); ?></h3>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($action === 'users'): ?>
<h2 class="mb-4">用户管理</h2>
<div class="card p-3">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>余额 (USDT)</th>
<th>认证状态</th>
<th>输赢控制</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$users = db()->query("SELECT * FROM accounts ORDER BY id DESC")->fetchAll();
foreach ($users as $u):
?>
<tr>
<td><?php echo $u['id']; ?></td>
<td><?php echo $u['username']; ?></td>
<td><?php echo number_format($u['balance'], 2); ?></td>
<td>
<span class="badge bg-<?php echo $u['kyc_status'] === 'VERIFIED' ? 'success' : ($u['kyc_status'] === 'PENDING' ? 'warning' : 'secondary'); ?>">
<?php echo $u['kyc_status']; ?>
</span>
</td>
<td>
<span class="badge bg-<?php echo $u['win_loss_control'] == 1 ? 'success' : ($u['win_loss_control'] == -1 ? 'danger' : 'secondary'); ?>">
<?php echo $u['win_loss_control'] == 1 ? '必赢' : ($u['win_loss_control'] == -1 ? '必输' : '正常'); ?>
</span>
</td>
<td>
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editUser<?php echo $u['id']; ?>">编辑</button>
</td>
</tr>
<!-- Modal -->
<div class="modal fade" id="editUser<?php echo $u['id']; ?>" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑用户: <?php echo $u['username']; ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form method="POST" class="mb-3">
<input type="hidden" name="account_id" value="<?php echo $u['id']; ?>">
<div class="mb-3">
<label class="form-label">调整余额</label>
<div class="input-group">
<input type="number" step="0.01" name="balance" class="form-control" value="<?php echo $u['balance']; ?>">
<button class="btn btn-primary" name="update_balance">更新余额</button>
</div>
</div>
</form>
<form method="POST" class="mb-3">
<input type="hidden" name="account_id" value="<?php echo $u['id']; ?>">
<div class="mb-3">
<label class="form-label">输赢控制</label>
<select name="win_loss_control" class="form-select mb-2">
<option value="0" <?php echo $u['win_loss_control'] == 0 ? 'selected' : ''; ?>>正常 (随机)</option>
<option value="1" <?php echo $u['win_loss_control'] == 1 ? 'selected' : ''; ?>>必赢 (Always Win)</option>
<option value="-1" <?php echo $u['win_loss_control'] == -1 ? 'selected' : ''; ?>>必输 (Always Loss)</option>
</select>
<button class="btn btn-warning w-100" name="update_win_loss">应用控制</button>
</div>
</form>
<form method="POST">
<input type="hidden" name="account_id" value="<?php echo $u['id']; ?>">
<div class="mb-3">
<label class="form-label">认证状态</label>
<select name="kyc_status" class="form-select mb-2">
<option value="UNVERIFIED" <?php echo $u['kyc_status'] === 'UNVERIFIED' ? 'selected' : ''; ?>>未认证</option>
<option value="PENDING" <?php echo $u['kyc_status'] === 'PENDING' ? 'selected' : ''; ?>>待审核</option>
<option value="VERIFIED" <?php echo $u['kyc_status'] === 'VERIFIED' ? 'selected' : ''; ?>>已认证</option>
</select>
<button class="btn btn-info w-100 text-white" name="update_kyc">更新状态</button>
</div>
</form>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if ($action === 'transactions'): ?>
<h2 class="mb-4">充值提现审核</h2>
<div class="card p-3">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>用户</th>
<th>类型</th>
<th>金额</th>
<th>哈希/地址</th>
<th>状态</th>
<th>日期</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$trxs = db()->query("SELECT t.*, a.username FROM transactions t JOIN accounts a ON t.account_id = a.id ORDER BY t.id DESC")->fetchAll();
foreach ($trxs as $t):
?>
<tr>
<td><?php echo $t['id']; ?></td>
<td><?php echo $t['username']; ?></td>
<td><span class="badge bg-<?php echo $t['transaction_type'] === 'deposit' ? 'primary' : 'warning'; ?>"><?php echo strtoupper($t['transaction_type']); ?></span></td>
<td><?php echo $t['amount']; ?></td>
<td><small class="text-truncate" style="max-width: 150px; display: inline-block;"><?php echo $t['tx_hash']; ?></small></td>
<td>
<span class="badge bg-<?php echo $t['status'] === 'completed' ? 'success' : ($t['status'] === 'pending' ? 'info' : 'danger'); ?>">
<?php echo strtoupper($t['status']); ?>
</span>
</td>
<td><?php echo $t['timestamp']; ?></td>
<td>
<?php if ($t['status'] === 'pending'): ?>
<form method="POST" class="d-inline">
<input type="hidden" name="transaction_id" value="<?php echo $t['id']; ?>">
<button class="btn btn-sm btn-success" name="<?php echo $t['transaction_type'] === 'deposit' ? 'approve_deposit' : 'approve_withdraw'; ?>">批准</button>
<button class="btn btn-sm btn-danger" name="reject_transaction">拒绝</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if ($action === 'orders'): ?>
<h2 class="mb-4">所有交易记录</h2>
<div class="card p-3">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>用户</th>
<th>币种</th>
<th>类型</th>
<th>方向</th>
<th>价格</th>
<th>数量</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<?php
$orders = db()->query("SELECT o.*, a.username FROM orders o JOIN accounts a ON o.account_id = a.id ORDER BY o.id DESC LIMIT 50")->fetchAll();
foreach ($orders as $o):
?>
<tr>
<td><?php echo $o['username']; ?></td>
<td><?php echo $o['symbol']; ?></td>
<td><?php echo $o['trade_type']; ?></td>
<td class="text-<?php echo $o['side'] === 'BUY' ? 'success' : 'danger'; ?>"><?php echo $o['side']; ?></td>
<td><?php echo $o['price']; ?></td>
<td><?php echo $o['amount']; ?></td>
<td><?php echo $o['created_at']; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if ($action === 'market'): ?>
<h2 class="mb-4">市场与币种管理</h2>
<div class="row">
<?php
$coins = db()->query("SELECT * FROM cryptocurrencies")->fetchAll();
foreach ($coins as $c):
?>
<div class="col-md-4">
<div class="card p-3">
<h5><?php echo $c['name']; ?> (<?php echo $c['symbol']; ?>)</h5>
<p class="mb-1">当前市场价: <?php echo $c['current_price']; ?></p>
<form method="POST">
<input type="hidden" name="coin_id" value="<?php echo $c['id']; ?>">
<div class="mb-2">
<label class="form-label small text-muted">手动价格 (0为跟随市场)</label>
<input type="number" step="0.000001" name="manual_price" class="form-control" value="<?php echo $c['manual_price']; ?>">
</div>
<button class="btn btn-sm btn-primary w-100" name="update_price">设置手动价格</button>
</form>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($action === 'settings'): ?>
<h2 class="mb-4">系统全局设置</h2>
<div class="card p-4 shadow-sm">
<form method="POST">
<div class="mb-3">
<label class="form-label">站点名称</label>
<input type="text" name="site_name" class="form-control" value="<?php echo $settings['site_name']; ?>">
</div>
<div class="mb-3">
<label class="form-label">联系邮箱</label>
<input type="email" name="contact_email" class="form-control" value="<?php echo $settings['contact_email']; ?>">
</div>
<div class="mb-3">
<label class="form-label">USDT 充值地址</label>
<input type="text" name="deposit_address" class="form-control" value="<?php echo $settings['deposit_address']; ?>">
<div class="form-text">用户在充值页面看到的钱包地址</div>
</div>
<button type="submit" name="update_site_settings" class="btn btn-primary px-5">保存所有设置</button>
</form>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

55
admin_login.php Normal file
View File

@ -0,0 +1,55 @@
<?php
include_once 'config.php';
if (isset($_SESSION['admin_id'])) {
header("Location: admin.php");
exit;
}
$error = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
$stmt = db()->prepare("SELECT * FROM admins WHERE username = ?");
$stmt->execute([$username]);
$admin = $stmt->fetch();
if ($admin && password_verify($password, $admin['password'])) {
$_SESSION['admin_id'] = $admin['id'];
header("Location: admin.php");
exit;
} else {
$error = "用户名或密码错误";
}
}
?>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>管理员登录</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background: #121212; color: white; display: flex; align-items: center; justify-content: center; height: 100vh; }
.login-card { background: #1e1e1e; padding: 2rem; border-radius: 1rem; width: 100%; max-width: 400px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
</style>
</head>
<body>
<div class="login-card">
<h3 class="text-center mb-4 text-warning">后台管理系统</h3>
<?php if($error): ?><div class="alert alert-danger"><?php echo $error; ?></div><?php endif; ?>
<form method="POST">
<div class="mb-3">
<label class="form-label">用户名</label>
<input type="text" name="username" class="form-control bg-dark text-white border-secondary" required>
</div>
<div class="mb-3">
<label class="form-label">密码</label>
<input type="password" name="password" class="form-control bg-dark text-white border-secondary" required>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-2">登录</button>
</form>
</div>
</body>
</html>

364
api.php Normal file
View File

@ -0,0 +1,364 @@
<?php
include_once 'config.php';
$action = $_GET['action'] ?? '';
/**
* Fetch prices from Binance with caching and high precision
*/
function get_real_prices() {
$cache_file = __DIR__ . '/db/price_cache.json';
$cache_time = 2; // Cache for 2 seconds
// Check cache
if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) {
$cache_data = json_decode(file_get_contents($cache_file), true);
if (!empty($cache_data)) return $cache_data;
}
// Fetch active coins from DB
try {
$stmt = db()->query("SELECT symbol FROM cryptocurrencies WHERE is_active = 1");
$symbols = $stmt->fetchAll(PDO::FETCH_COLUMN);
} catch (Exception $e) {
$symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'DOGEUSDT'];
}
if (empty($symbols)) $symbols = ['BTCUSDT'];
// Use Binance 24hr ticker for comprehensive data
$symbols_json = json_encode($symbols);
$url = "https://api.binance.com/api/v3/ticker/24hr?symbols=" . urlencode($symbols_json);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
// Disable SSL verification if needed for some environments, but prefer keeping it
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$prices = [];
if ($http_code == 200 && $response) {
$data = json_decode($response, true);
if (is_array($data)) {
foreach ($data as $item) {
if (isset($item['symbol'])) {
$prices[$item['symbol']] = [
'price' => $item['lastPrice'],
'change' => $item['priceChangePercent'],
'high' => $item['highPrice'],
'low' => $item['lowPrice'],
'volume' => $item['quoteVolume'],
'ts' => time()
];
}
}
}
}
// Fallback: If 24hr fails, try simpler price-only endpoint
if (empty($prices)) {
$url_simple = "https://api.binance.com/api/v3/ticker/price";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url_simple);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$resp_simple = curl_exec($ch);
curl_close($ch);
if ($resp_simple) {
$data_simple = json_decode($resp_simple, true);
if (is_array($data_simple)) {
foreach ($data_simple as $item) {
if (in_array($item['symbol'], $symbols)) {
$prices[$item['symbol']] = [
'price' => $item['price'],
'change' => '0.00',
'high' => $item['price'],
'low' => $item['price'],
'volume' => '0',
'ts' => time()
];
}
}
}
}
}
if (!empty($prices)) {
// Only update file if we have new data
@file_put_contents($cache_file, json_encode($prices));
} else if (file_exists($cache_file)) {
// Last resort: return expired cache
return json_decode(file_get_contents($cache_file), true);
}
return $prices;
}
if ($action === 'market_data') {
$real_prices = get_real_prices();
try {
$stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1 ORDER BY id ASC");
$coins = $stmt->fetchAll();
} catch (Exception $e) {
$coins = [];
}
$updated_coins = [];
foreach ($coins as $coin) {
$symbol = $coin['symbol'];
if (isset($real_prices[$symbol])) {
$coin['price'] = (string)$real_prices[$symbol]['price']; // Keep as string for precision
$coin['change'] = (float)$real_prices[$symbol]['change'];
$coin['high'] = (string)$real_prices[$symbol]['high'];
$coin['low'] = (string)$real_prices[$symbol]['low'];
$coin['volume'] = (float)$real_prices[$symbol]['volume'];
if ($coin['manual_price'] > 0) {
$coin['price'] = (string)$coin['manual_price'];
}
// Sync to DB occasionally (logic can be improved, but this is current)
$upd = db()->prepare("UPDATE cryptocurrencies SET current_price = ?, change_24h = ? WHERE id = ?");
$upd->execute([$coin['price'], $coin['change'], $coin['id']]);
} else {
$coin['price'] = (string)$coin['current_price'];
$coin['change'] = (float)$coin['change_24h'];
$coin['high'] = (string)($coin['current_price'] * 1.01);
$coin['low'] = (string)($coin['current_price'] * 0.99);
$coin['volume'] = 0;
}
$updated_coins[] = $coin;
}
header('Content-Type: application/json');
echo json_encode($updated_coins);
exit;
}
if ($action === 'submit_order') {
check_auth();
$data = json_decode(file_get_contents('php://input'), true);
if (!$data) {
echo json_encode(['status' => 'error', 'message' => '无效请求数据']);
exit;
}
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
$symbol = $data['symbol'] ?? 'BTCUSDT';
$side = $data['side'] ?? 'BUY';
$trade_type = strtoupper($data['trade_type'] ?? 'SPOT');
$amount = (float)($data['amount'] ?? 0);
$leverage = (int)($data['leverage'] ?? 1);
if ($amount <= 0) {
echo json_encode(['status' => 'error', 'message' => '请输入有效数量']);
exit;
}
$real_prices = get_real_prices();
$stmt = db()->prepare("SELECT * FROM cryptocurrencies WHERE symbol = ?");
$stmt->execute([$symbol]);
$coin = $stmt->fetch();
if (!$coin) {
echo json_encode(['status' => 'error', 'message' => '不支持该币种']);
exit;
}
if ($coin['manual_price'] > 0) {
$current_price = (float)$coin['manual_price'];
} elseif (isset($real_prices[$symbol])) {
$current_price = (float)$real_prices[$symbol]['price'];
} else {
$current_price = (float)$coin['current_price'];
}
if ($current_price <= 0) {
echo json_encode(['status' => 'error', 'message' => '价格获取失败,请重试']);
exit;
}
try {
$db = db();
$db->beginTransaction();
if ($trade_type === 'SPOT') {
if ($side === 'BUY') {
$total_cost = $amount * $current_price;
if ($account['balance'] < $total_cost) {
throw new Exception('余额不足 (需要 ' . number_format($total_cost, 2) . ' USDT)');
}
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
$stmt->execute([$total_cost, $account['id']]);
$currency = str_replace('USDT', '', $symbol);
$stmt = $db->prepare("INSERT INTO assets (account_id, currency, balance) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE balance = balance + ?");
$stmt->execute([$account['id'], $currency, $amount, $amount]);
} else { // SELL
$currency = str_replace('USDT', '', $symbol);
$stmt = $db->prepare("SELECT balance FROM assets WHERE account_id = ? AND currency = ?");
$stmt->execute([$account['id'], $currency]);
$asset = $stmt->fetch();
if (!$asset || $asset['balance'] < $amount) {
throw new Exception('资产余额不足');
}
$stmt = $db->prepare("UPDATE assets SET balance = balance - ? WHERE account_id = ? AND currency = ?");
$stmt->execute([$amount, $account['id'], $currency]);
$total_gain = $amount * $current_price;
$stmt = $db->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
$stmt->execute([$total_gain, $account['id']]);
}
$stmt = $db->prepare("INSERT INTO orders (account_id, symbol, trade_type, side, order_type, price, amount, total_usdt, status) VALUES (?, ?, 'SPOT', ?, 'MARKET', ?, ?, ?, 'FILLED')");
$stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $amount * $current_price]);
} else if ($trade_type === 'CONTRACT') {
$contract_value = 100;
$total_value = $amount * $contract_value;
$margin = $total_value / $leverage;
if ($account['balance'] < $margin) {
throw new Exception('保证金不足 (需要 ' . number_format($margin, 2) . ' USDT)');
}
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
$stmt->execute([$margin, $account['id']]);
$stmt = $db->prepare("INSERT INTO positions (account_id, symbol, side, leverage, entry_price, lots, margin) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$account['id'], $symbol, ($side === 'BUY' ? 'LONG' : 'SHORT'), $leverage, $current_price, $amount, $margin]);
$stmt = $db->prepare("INSERT INTO orders (account_id, symbol, trade_type, side, order_type, price, amount, leverage, status) VALUES (?, ?, 'CONTRACT', ?, 'MARKET', ?, ?, ?, 'FILLED')");
$stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $leverage]);
}
$db->commit();
echo json_encode(['status' => 'success', 'message' => '交易成功']);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
exit;
}
if ($action === 'positions') {
check_auth();
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
$stmt = db()->prepare("SELECT * FROM positions WHERE account_id = ? AND is_active = 1");
$stmt->execute([$account['id']]);
$positions = $stmt->fetchAll();
$real_prices = get_real_prices();
foreach ($positions as &$pos) {
$symbol = $pos['symbol'];
$stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?");
$stmt->execute([$symbol]);
$coin = $stmt->fetch();
if ($coin && $coin['manual_price'] > 0) {
$current_price = (float)$coin['manual_price'];
} elseif (isset($real_prices[$symbol])) {
$current_price = (float)$real_prices[$symbol]['price'];
} else {
$current_price = (float)$pos['entry_price'];
}
$pos['current_price'] = $current_price;
if ($pos['side'] === 'LONG') {
$pos['pnl'] = (($current_price - $pos['entry_price']) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
} else {
$pos['pnl'] = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
}
if ($account['win_loss_control'] == 1 && $pos['pnl'] < 0) {
$pos['pnl'] = abs($pos['pnl']) * 0.2;
} else if ($account['win_loss_control'] == -1 && $pos['pnl'] > 0) {
$pos['pnl'] = -abs($pos['pnl']) * 1.5;
}
}
echo json_encode($positions);
exit;
}
if ($action === 'close_position') {
check_auth();
$data = json_decode(file_get_contents('php://input'), true);
$pos_id = $data['id'] ?? 0;
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
try {
$db = db();
$db->beginTransaction();
$stmt = $db->prepare("SELECT * FROM positions WHERE id = ? AND account_id = ? AND is_active = 1");
$stmt->execute([$pos_id, $account['id']]);
$pos = $stmt->fetch();
if (!$pos) throw new Exception('仓位不存在');
$symbol = $pos['symbol'];
$real_prices = get_real_prices();
$stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?");
$stmt->execute([$symbol]);
$coin = $stmt->fetch();
if ($coin && $coin['manual_price'] > 0) {
$current_price = (float)$coin['manual_price'];
} elseif (isset($real_prices[$symbol])) {
$current_price = (float)$real_prices[$symbol]['price'];
} else {
$current_price = (float)$pos['entry_price'];
}
if ($pos['side'] === 'LONG') {
$pnl = (($current_price - $pos['entry_price']) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
} else {
$pnl = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage'];
}
if ($account['win_loss_control'] == 1) {
if ($pnl < 0) $pnl = abs($pnl) * 0.1;
} else if ($account['win_loss_control'] == -1) {
if ($pnl > 0) $pnl = -abs($pnl) * 1.2;
}
$payout = $pos['margin'] + $pnl;
if ($payout < 0) $payout = 0;
$stmt = $db->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
$stmt->execute([$payout, $account['id']]);
$stmt = $db->prepare("UPDATE positions SET is_active = 0 WHERE id = ?");
$stmt->execute([$pos_id]);
$db->commit();
echo json_encode(['status' => 'success', 'message' => '平仓成功']);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
exit;
}
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

28
config.php Normal file
View File

@ -0,0 +1,28 @@
<?php
// PHP Core Config
session_start();
// Include the existing database configuration
require_once __DIR__ . '/db/config.php';
// Helper: Check if user is logged in
function check_auth() {
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit;
}
}
// Helper: Get user account
function get_account($user_id) {
$stmt = db()->prepare("SELECT * FROM accounts WHERE user_id = ?");
$stmt->execute([$user_id]);
return $stmt->fetch();
}
// Helper: Get site settings
function get_site_settings() {
$stmt = db()->query("SELECT * FROM site_settings LIMIT 1");
return $stmt->fetch();
}
?>

View File

@ -1,52 +1,16 @@
"""
Django settings for config project.
Generated by 'django-admin startproject' using Django 5.2.7.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(BASE_DIR.parent / ".env")
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "change-me")
DEBUG = os.getenv("DJANGO_DEBUG", "true").lower() == "true"
SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-default-key')
ALLOWED_HOSTS = [
"127.0.0.1",
"localhost",
os.getenv("HOST_FQDN", ""),
]
DEBUG = True
CSRF_TRUSTED_ORIGINS = [
origin for origin in [
os.getenv("HOST_FQDN", ""),
os.getenv("CSRF_TRUSTED_ORIGIN", "")
] if origin
]
CSRF_TRUSTED_ORIGINS = [
f"https://{host}" if not host.startswith(("http://", "https://")) else host
for host in CSRF_TRUSTED_ORIGINS
]
# Cookies must always be HTTPS-only; SameSite=Lax keeps CSRF working behind the proxy.
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SAMESITE = "None"
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# Application definition
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
@ -65,25 +29,22 @@ MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Disable X-Frame-Options middleware to allow Flatlogic preview iframes.
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
X_FRAME_OPTIONS = 'ALLOWALL'
ROOT_URLCONF = 'config.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# IMPORTANT: do not remove injects PROJECT_DESCRIPTION/PROJECT_IMAGE_URL and cache-busting timestamp
'core.context_processors.project_context',
],
},
@ -92,91 +53,34 @@ TEMPLATES = [
WSGI_APPLICATION = 'config.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': os.getenv('DB_NAME', ''),
'USER': os.getenv('DB_USER', ''),
'PASSWORD': os.getenv('DB_PASS', ''),
'NAME': os.getenv('DB_NAME', 'django_db'),
'USER': os.getenv('DB_USER', 'django_user'),
'PASSWORD': os.getenv('DB_PASS', 'django_pass'),
'HOST': os.getenv('DB_HOST', '127.0.0.1'),
'PORT': os.getenv('DB_PORT', '3306'),
'OPTIONS': {
'charset': 'utf8mb4',
},
},
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
# Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS.
STATICFILES_DIRS = [BASE_DIR / 'static']
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
BASE_DIR / 'static',
BASE_DIR / 'assets',
BASE_DIR / 'node_modules',
]
# Email
EMAIL_BACKEND = os.getenv(
"EMAIL_BACKEND",
"django.core.mail.backends.smtp.EmailBackend"
)
EMAIL_HOST = os.getenv("EMAIL_HOST", "127.0.0.1")
EMAIL_PORT = int(os.getenv("EMAIL_PORT", "587"))
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "")
EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "true").lower() == "true"
EMAIL_USE_SSL = os.getenv("EMAIL_USE_SSL", "false").lower() == "true"
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "no-reply@example.com")
CONTACT_EMAIL_TO = [
item.strip()
for item in os.getenv("CONTACT_EMAIL_TO", DEFAULT_FROM_EMAIL).split(",")
if item.strip()
]
# When both TLS and SSL flags are enabled, prefer SSL explicitly
if EMAIL_USE_SSL:
EMAIL_USE_TLS = False
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'

View File

@ -1,3 +1,34 @@
from django.contrib import admin
from .models import Cryptocurrency, Account, Asset, Order, Transaction, SiteSettings
# Register your models here.
@admin.register(SiteSettings)
class SiteSettingsAdmin(admin.ModelAdmin):
list_display = ('site_name', 'customer_service_url', 'is_pinning_active')
@admin.register(Cryptocurrency)
class CryptocurrencyAdmin(admin.ModelAdmin):
list_display = ('symbol', 'name', 'current_price', 'manual_price', 'change_24h', 'is_active')
search_fields = ('symbol', 'name')
list_editable = ('manual_price', 'is_active')
@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):
list_display = ('uid', 'user', 'balance', 'credit_score', 'kyc_status', 'win_loss_control', 'created_at')
search_fields = ('uid', 'user__username')
list_filter = ('kyc_status', 'account_type')
list_editable = ('win_loss_control',)
@admin.register(Asset)
class AssetAdmin(admin.ModelAdmin):
list_display = ('account', 'currency', 'balance', 'frozen')
list_filter = ('currency',)
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('account', 'symbol', 'trade_type', 'side', 'status', 'created_at')
list_filter = ('trade_type', 'side', 'status')
@admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
list_display = ('account', 'transaction_type', 'amount', 'currency', 'status', 'timestamp')
list_filter = ('transaction_type', 'status')

View File

@ -1,13 +1,14 @@
import os
import time
from .models import SiteSettings
def project_context(request):
"""
Adds project-specific environment variables to the template context globally.
"""
settings = SiteSettings.objects.first()
if not settings:
settings = SiteSettings.objects.create(site_name="BitCrypto")
return {
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
# Used for cache-busting static assets
"deployment_timestamp": int(time.time()),
'site_settings': settings,
'project_name': settings.site_name,
'project_description': "全球领先的数字资产交易平台"
}
# Alias for compatibility if needed
site_settings = project_context

View File

Binary file not shown.

View File

View File

@ -0,0 +1,71 @@
from django.core.management.base import BaseCommand
from core.models import Cryptocurrency
from decimal import Decimal
class Command(BaseCommand):
help = 'Seeds the database with common cryptocurrencies'
def handle(self, *args, **options):
cryptos = [
('BTC', 'Bitcoin', 65000.00),
('ETH', 'Ethereum', 3500.00),
('BNB', 'BNB', 600.00),
('SOL', 'Solana', 150.00),
('XRP', 'XRP', 0.60),
('ADA', 'Cardano', 0.50),
('AVAX', 'Avalanche', 40.00),
('DOT', 'Polkadot', 7.00),
('DOGE', 'Dogecoin', 0.15),
('SHIB', 'Shiba Inu', 0.000025),
('MATIC', 'Polygon', 0.70),
('LINK', 'Chainlink', 15.00),
('UNI', 'Uniswap', 8.00),
('LTC', 'Litecoin', 80.00),
('BCH', 'Bitcoin Cash', 450.00),
('ATOM', 'Cosmos', 10.00),
('XLM', 'Stellar', 0.12),
('ETC', 'Ethereum Classic', 30.00),
('NEAR', 'NEAR Protocol', 6.00),
('FIL', 'Filecoin', 6.00),
('ICP', 'Internet Computer', 12.00),
('HBAR', 'Hedera', 0.10),
('VET', 'VeChain', 0.04),
('ALGO', 'Algorand', 0.20),
('GRT', 'The Graph', 0.30),
('FTM', 'Fantom', 0.80),
('SAND', 'The Sandbox', 0.50),
('MANA', 'Decentraland', 0.50),
('AAVE', 'Aave', 100.00),
('THETA', 'Theta Network', 2.50),
('EGLD', 'MultiversX', 45.00),
('XTZ', 'Tezos', 1.20),
('EOS', 'EOS', 0.80),
('FLOW', 'Flow', 1.00),
('CHZ', 'Chiliz', 0.15),
('AXS', 'Axie Infinity', 8.00),
('GALA', 'Gala', 0.05),
('KAVA', 'Kava', 0.70),
('ZEC', 'Zcash', 30.00),
('DASH', 'Dash', 35.00),
('NEO', 'NEO', 15.00),
('IOTA', 'IOTA', 0.25),
('KLAY', 'Klaytn', 0.20),
('BSV', 'Bitcoin SV', 70.00),
('MINA', 'Mina', 0.90),
('XEC', 'eCash', 0.00005),
('BTT', 'BitTorrent', 0.000001),
('LUNC', 'Terra Classic', 0.0001),
('USTC', 'TerraClassicUSD', 0.02),
]
for symbol, name, price in cryptos:
Cryptocurrency.objects.update_or_create(
symbol=symbol,
defaults={
'name': name,
'current_price': Decimal(str(price)),
'change_24h': Decimal('0.00'),
'is_active': True
}
)
self.stdout.write(self.style.SUCCESS(f'Successfully seeded {symbol}'))

View File

@ -0,0 +1,25 @@
from django.core.management.base import BaseCommand
from core.models import SiteSettings, Cryptocurrency
import decimal
class Command(BaseCommand):
help = 'Seed initial site settings and data'
def handle(self, *args, **options):
# Site Settings
settings, created = SiteSettings.objects.get_or_create(id=1)
settings.site_name = "BitCrypto"
settings.customer_service_url = "https://t.me/bitcrypto_support"
settings.terms_content = "欢迎使用 BitCrypto。通过访问我们的平台您同意遵守以下条款1. 用户必须年满 18 岁。2. 您对账户的安全负全部责任。3. 加密货币交易具有高度风险..."
settings.privacy_content = "我们重视您的隐私。BitCrypto 仅收集必要的个人信息以提供服务。我们使用先进的加密技术保护您的数据,绝不向第三方出售您的个人信息。"
settings.save()
self.stdout.write(self.style.SUCCESS('Successfully seeded site settings'))
# Ensure BTC exists
btc, created = Cryptocurrency.objects.get_or_create(symbol="BTC")
btc.name = "Bitcoin"
btc.current_price = decimal.Decimal("48000.00")
btc.save()
self.stdout.write(self.style.SUCCESS('Successfully seeded cryptocurrencies'))

View File

@ -0,0 +1,71 @@
# Generated by Django 5.2.7 on 2026-02-06 06:28
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Account',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
('session_key', models.CharField(blank=True, max_length=100, null=True, unique=True)),
('account_type', models.CharField(choices=[('SIMULATED', 'Simulated'), ('REAL', 'Real')], default='SIMULATED', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Ledger',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('currency', models.CharField(max_length=10)),
('amount', models.DecimalField(decimal_places=8, max_digits=30)),
('balance_before', models.DecimalField(decimal_places=8, max_digits=30)),
('balance_after', models.DecimalField(decimal_places=8, max_digits=30)),
('biz_type', models.CharField(max_length=50)),
('reference_id', models.CharField(blank=True, max_length=100, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ledger_entries', to='core.account')),
],
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symbol', models.CharField(default='BTC-USDT', max_length=20)),
('side', models.CharField(choices=[('BUY', 'Buy'), ('SELL', 'Sell')], max_length=10)),
('order_type', models.CharField(choices=[('LIMIT', 'Limit'), ('MARKET', 'Market')], max_length=10)),
('price', models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True)),
('amount', models.DecimalField(decimal_places=8, max_digits=30)),
('filled_amount', models.DecimalField(decimal_places=8, default=0, max_digits=30)),
('status', models.CharField(choices=[('LIVE', 'Live'), ('PARTIALLY_FILLED', 'Partially Filled'), ('FILLED', 'Filled'), ('CANCELED', 'Canceled')], default='LIVE', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='core.account')),
],
),
migrations.CreateModel(
name='Asset',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('currency', models.CharField(max_length=10)),
('balance', models.DecimalField(decimal_places=8, default=0, max_digits=30)),
('frozen', models.DecimalField(decimal_places=8, default=0, max_digits=30)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='core.account')),
],
options={
'unique_together': {('account', 'currency')},
},
),
]

View File

@ -0,0 +1,151 @@
# Generated by Django 5.2.7 on 2026-02-06 07:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Cryptocurrency',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symbol', models.CharField(max_length=20, unique=True, verbose_name='币种代码')),
('name', models.CharField(max_length=100, verbose_name='币种名称')),
('icon_url', models.URLField(blank=True, null=True, verbose_name='图标URL')),
('current_price', models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='当前价格')),
('manual_price', models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='插针价格 (留空则使用当前价格)')),
('change_24h', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='24h 涨跌幅')),
('is_active', models.BooleanField(default=True, verbose_name='是否启用')),
],
options={
'verbose_name': '加密货币',
'verbose_name_plural': '加密货币管理',
},
),
migrations.AlterModelOptions(
name='account',
options={'verbose_name': '账户', 'verbose_name_plural': '账户管理'},
),
migrations.AlterModelOptions(
name='asset',
options={'verbose_name': '资产', 'verbose_name_plural': '资产管理'},
),
migrations.AlterModelOptions(
name='ledger',
options={'verbose_name': '账单', 'verbose_name_plural': '账单管理'},
),
migrations.AlterModelOptions(
name='order',
options={'verbose_name': '订单', 'verbose_name_plural': '订单管理'},
),
migrations.AddField(
model_name='account',
name='win_loss_control',
field=models.IntegerField(default=0, help_text='控制胜率: -100 (必输) 到 100 (必赢), 0 为随机', verbose_name='输赢控制'),
),
migrations.AlterField(
model_name='account',
name='account_type',
field=models.CharField(choices=[('SIMULATED', '模拟账户'), ('REAL', '真实账户')], default='SIMULATED', max_length=20, verbose_name='账户类型'),
),
migrations.AlterField(
model_name='account',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
),
migrations.AlterField(
model_name='asset',
name='balance',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='余额'),
),
migrations.AlterField(
model_name='asset',
name='currency',
field=models.CharField(max_length=10, verbose_name='币种'),
),
migrations.AlterField(
model_name='asset',
name='frozen',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='冻结'),
),
migrations.AlterField(
model_name='ledger',
name='amount',
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='金额'),
),
migrations.AlterField(
model_name='ledger',
name='balance_after',
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='变动后余额'),
),
migrations.AlterField(
model_name='ledger',
name='balance_before',
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='变动前余额'),
),
migrations.AlterField(
model_name='ledger',
name='biz_type',
field=models.CharField(max_length=50, verbose_name='业务类型'),
),
migrations.AlterField(
model_name='ledger',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
),
migrations.AlterField(
model_name='ledger',
name='currency',
field=models.CharField(max_length=10, verbose_name='币种'),
),
migrations.AlterField(
model_name='ledger',
name='reference_id',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='引用ID'),
),
migrations.AlterField(
model_name='order',
name='amount',
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='数量'),
),
migrations.AlterField(
model_name='order',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
),
migrations.AlterField(
model_name='order',
name='filled_amount',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='已成交数量'),
),
migrations.AlterField(
model_name='order',
name='order_type',
field=models.CharField(choices=[('LIMIT', '限价'), ('MARKET', '市价')], max_length=10, verbose_name='类型'),
),
migrations.AlterField(
model_name='order',
name='price',
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='价格'),
),
migrations.AlterField(
model_name='order',
name='side',
field=models.CharField(choices=[('BUY', '买入'), ('SELL', '卖出')], max_length=10, verbose_name='方向'),
),
migrations.AlterField(
model_name='order',
name='status',
field=models.CharField(choices=[('LIVE', '进行中'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销')], default='LIVE', max_length=20, verbose_name='状态'),
),
migrations.AlterField(
model_name='order',
name='symbol',
field=models.CharField(default='BTC-USDT', max_length=20, verbose_name='交易对'),
),
]

View File

@ -0,0 +1,92 @@
# Generated by Django 5.2.7 on 2026-02-06 07:19
import core.models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_cryptocurrency_alter_account_options_and_more'),
]
operations = [
migrations.AddField(
model_name='account',
name='credit_score',
field=models.IntegerField(default=80, verbose_name='信用分'),
),
migrations.AddField(
model_name='account',
name='kyc_status',
field=models.CharField(choices=[('UNVERIFIED', '未认证'), ('PENDING', '审核中'), ('VERIFIED', '已认证'), ('REJECTED', '已拒绝')], default='UNVERIFIED', max_length=20, verbose_name='实名认证状态'),
),
migrations.AddField(
model_name='account',
name='language',
field=models.CharField(default='en', max_length=10, verbose_name='语言偏好'),
),
migrations.AddField(
model_name='order',
name='close_price',
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='平仓价格'),
),
migrations.AddField(
model_name='order',
name='entry_price',
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='入场价格'),
),
migrations.AddField(
model_name='order',
name='leverage',
field=models.IntegerField(default=1, verbose_name='杠杆倍数'),
),
migrations.AddField(
model_name='order',
name='profit_loss',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='盈亏'),
),
migrations.AddField(
model_name='order',
name='trade_type',
field=models.CharField(choices=[('SPOT', '现货'), ('CONTRACT', '合约')], default='SPOT', max_length=20, verbose_name='交易类型'),
),
migrations.AlterField(
model_name='account',
name='uid',
field=models.CharField(default=core.models.generate_uid, max_length=6, unique=True, verbose_name='UID'),
),
migrations.AlterField(
model_name='order',
name='price',
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='委托价格'),
),
migrations.AlterField(
model_name='order',
name='side',
field=models.CharField(choices=[('BUY', '买入/做多'), ('SELL', '卖出/做空')], max_length=10, verbose_name='方向'),
),
migrations.AlterField(
model_name='order',
name='status',
field=models.CharField(choices=[('LIVE', '进行中'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销'), ('CLOSED', '已平仓')], default='LIVE', max_length=20, verbose_name='状态'),
),
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('tx_type', models.CharField(choices=[('DEPOSIT', '充值'), ('WITHDRAW', '提现')], max_length=20, verbose_name='类型')),
('currency', models.CharField(default='USDT', max_length=10, verbose_name='币种')),
('amount', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='金额')),
('address', models.CharField(blank=True, max_length=255, null=True, verbose_name='地址/流水')),
('status', models.CharField(choices=[('PENDING', '待处理'), ('SUCCESS', '成功'), ('FAILED', '失败')], default='PENDING', max_length=20, verbose_name='状态')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='时间')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to='core.account')),
],
options={
'verbose_name': '充提记录',
'verbose_name_plural': '充提管理',
},
),
]

View File

@ -0,0 +1,59 @@
# Generated by Django 5.2.7 on 2026-02-06 07:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_account_credit_score_account_kyc_status_and_more'),
]
operations = [
migrations.RenameField(
model_name='transaction',
old_name='created_at',
new_name='timestamp',
),
migrations.RemoveField(
model_name='transaction',
name='address',
),
migrations.RemoveField(
model_name='transaction',
name='tx_type',
),
migrations.AddField(
model_name='account',
name='balance',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='可用余额 (USDT)'),
),
migrations.AddField(
model_name='transaction',
name='transaction_type',
field=models.CharField(choices=[('deposit', '充值'), ('withdraw', '提现')], default='deposit', max_length=20, verbose_name='类型'),
),
migrations.AddField(
model_name='transaction',
name='tx_hash',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='交易哈希'),
),
migrations.AlterField(
model_name='account',
name='language',
field=models.CharField(default='zh-hans', max_length=10, verbose_name='语言偏好'),
),
migrations.AlterField(
model_name='transaction',
name='amount',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='金额'),
),
migrations.AlterField(
model_name='transaction',
name='status',
field=models.CharField(choices=[('pending', '待处理'), ('completed', '成功'), ('failed', '失败')], default='pending', max_length=20, verbose_name='状态'),
),
migrations.DeleteModel(
name='Ledger',
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 5.2.7 on 2026-02-06 09:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_rename_created_at_transaction_timestamp_and_more'),
]
operations = [
migrations.CreateModel(
name='SiteSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('site_name', models.CharField(default='BitCrypto', max_length=100, verbose_name='网站名称')),
('customer_service_url', models.URLField(blank=True, null=True, verbose_name='在线客服链接')),
('terms_content', models.TextField(blank=True, verbose_name='服务条款内容')),
('privacy_content', models.TextField(blank=True, verbose_name='隐私政策内容')),
('is_pinning_active', models.BooleanField(default=False, help_text='开启后,所有币种将向手动设置的价格靠拢', verbose_name='全局插针激活')),
],
options={
'verbose_name': '系统设置',
'verbose_name_plural': '系统设置',
},
),
]

View File

@ -0,0 +1,69 @@
# Generated by Django 5.2.7 on 2026-02-06 11:38
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_sitesettings'),
]
operations = [
migrations.RemoveField(
model_name='order',
name='close_price',
),
migrations.RemoveField(
model_name='order',
name='entry_price',
),
migrations.RemoveField(
model_name='order',
name='filled_amount',
),
migrations.RemoveField(
model_name='order',
name='profit_loss',
),
migrations.AddField(
model_name='account',
name='frozen_balance',
field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='冻结余额 (USDT)'),
),
migrations.AddField(
model_name='order',
name='total_usdt',
field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='成交额 (USDT)'),
),
migrations.AlterField(
model_name='order',
name='amount',
field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='数量/手数'),
),
migrations.AlterField(
model_name='order',
name='status',
field=models.CharField(choices=[('PENDING', '等待成交'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销')], default='PENDING', max_length=20, verbose_name='状态'),
),
migrations.CreateModel(
name='Position',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('symbol', models.CharField(max_length=20, verbose_name='交易对')),
('side', models.CharField(choices=[('LONG', '做多'), ('SHORT', '做空')], max_length=10, verbose_name='方向')),
('leverage', models.IntegerField(default=20, verbose_name='杠杆')),
('entry_price', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='开仓均价')),
('lots', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='手数')),
('margin', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='占用保证金')),
('is_active', models.BooleanField(default=True, verbose_name='是否持仓')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='时间')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='positions', to='core.account')),
],
options={
'verbose_name': '持仓',
'verbose_name_plural': '持仓管理',
},
),
]

View File

@ -1,3 +1,160 @@
import random
import string
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
# Create your models here.
def generate_uid():
return ''.join(random.choices(string.digits, k=6))
class SiteSettings(models.Model):
site_name = models.CharField(max_length=100, default="BitCrypto", verbose_name=_("网站名称"))
customer_service_url = models.URLField(blank=True, null=True, verbose_name=_("在线客服链接"))
terms_content = models.TextField(blank=True, verbose_name=_("服务条款内容"))
privacy_content = models.TextField(blank=True, verbose_name=_("隐私政策内容"))
# Global Market Control
is_pinning_active = models.BooleanField(default=False, verbose_name=_("全局插针激活"), help_text=_("开启后,所有币种将向手动设置的价格靠拢"))
class Meta:
verbose_name = _("系统设置")
verbose_name_plural = _("系统设置")
def __str__(self):
return self.site_name
class Cryptocurrency(models.Model):
symbol = models.CharField(max_length=20, unique=True, verbose_name=_("币种代码"))
name = models.CharField(max_length=100, verbose_name=_("币种名称"))
icon_url = models.URLField(null=True, blank=True, verbose_name=_("图标URL"))
current_price = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("当前价格"))
manual_price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("插针价格 (留空则使用当前价格)"))
change_24h = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name=_("24h 涨跌幅"))
is_active = models.BooleanField(default=True, verbose_name=_("是否启用"))
class Meta:
verbose_name = _("加密货币")
verbose_name_plural = _("加密货币管理")
def __str__(self):
return f"{self.symbol} - {self.name}"
class Account(models.Model):
ACCOUNT_TYPES = (
('SIMULATED', _('模拟账户')),
('REAL', _('真实账户')),
)
KYC_STATUS = (
('UNVERIFIED', _('未认证')),
('PENDING', _('审核中')),
('VERIFIED', _('已认证')),
('REJECTED', _('已拒绝')),
)
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
uid = models.CharField(max_length=6, unique=True, default=generate_uid, verbose_name=_("UID"))
session_key = models.CharField(max_length=100, null=True, blank=True, unique=True)
account_type = models.CharField(max_length=20, choices=ACCOUNT_TYPES, default='SIMULATED', verbose_name=_("账户类型"))
balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("可用余额 (USDT)"))
frozen_balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("冻结余额 (USDT)"))
credit_score = models.IntegerField(default=80, verbose_name=_("信用分"))
kyc_status = models.CharField(max_length=20, choices=KYC_STATUS, default='UNVERIFIED', verbose_name=_("实名认证状态"))
win_loss_control = models.IntegerField(default=0, help_text=_("控制胜率: -100 (必输) 到 100 (必赢), 0 为随机"), verbose_name=_("输赢控制"))
language = models.CharField(max_length=10, default='zh-hans', verbose_name=_("语言偏好"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间"))
class Meta:
verbose_name = _("账户")
verbose_name_plural = _("账户管理")
def __str__(self):
return f"Account {self.uid} ({self.get_account_type_display()})"
class Asset(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='assets')
currency = models.CharField(max_length=10, verbose_name=_("币种"))
balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("余额"))
frozen = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("冻结"))
class Meta:
unique_together = ('account', 'currency')
verbose_name = _("资产")
verbose_name_plural = _("资产管理")
@property
def total(self):
return self.balance + self.frozen
def __str__(self):
return f"{self.account.uid} - {self.currency}: {self.balance}"
class Order(models.Model):
SIDE_CHOICES = (('BUY', _('买入/做多')), ('SELL', _('卖出/做空')))
TYPE_CHOICES = (('LIMIT', _('限价')), ('MARKET', _('市价')))
TRADE_TYPE_CHOICES = (('SPOT', _('现货')), ('CONTRACT', _('合约')))
STATUS_CHOICES = (
('PENDING', _('等待成交')),
('PARTIALLY_FILLED', _('部分成交')),
('FILLED', _('已成交')),
('CANCELED', _('已撤销')),
)
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='orders')
symbol = models.CharField(max_length=20, default='BTC-USDT', verbose_name=_("交易对"))
trade_type = models.CharField(max_length=20, choices=TRADE_TYPE_CHOICES, default='SPOT', verbose_name=_("交易类型"))
side = models.CharField(max_length=10, choices=SIDE_CHOICES, verbose_name=_("方向"))
order_type = models.CharField(max_length=10, choices=TYPE_CHOICES, verbose_name=_("类型"))
price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("委托价格"))
amount = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("数量/手数"))
total_usdt = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("成交额 (USDT)"))
leverage = models.IntegerField(default=1, verbose_name=_("杠杆倍数"))
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING', verbose_name=_("状态"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间"))
class Meta:
verbose_name = _("订单")
verbose_name_plural = _("订单管理")
def __str__(self):
return f"{self.trade_type} {self.side} {self.amount} {self.symbol}"
class Position(models.Model):
SIDE_CHOICES = (('LONG', _('做多')), ('SHORT', _('做空')))
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='positions')
symbol = models.CharField(max_length=20, verbose_name=_("交易对"))
side = models.CharField(max_length=10, choices=SIDE_CHOICES, verbose_name=_("方向"))
leverage = models.IntegerField(default=20, verbose_name=_("杠杆"))
entry_price = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("开仓均价"))
lots = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("手数"))
margin = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("占用保证金"))
is_active = models.BooleanField(default=True, verbose_name=_("是否持仓"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("时间"))
class Meta:
verbose_name = _("持仓")
verbose_name_plural = _("持仓管理")
def __str__(self):
return f"{self.account.uid} {self.symbol} {self.side} {self.lots}"
class Transaction(models.Model):
TX_TYPE = (('deposit', _('充值')), ('withdraw', _('提现')))
TX_STATUS = (('pending', _('待处理')), ('completed', _('成功')), ('failed', _('失败')))
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='transactions')
transaction_type = models.CharField(max_length=20, choices=TX_TYPE, default='deposit', verbose_name=_("类型"))
currency = models.CharField(max_length=10, default='USDT', verbose_name=_("币种"))
amount = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("金额"))
tx_hash = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("交易哈希"))
status = models.CharField(max_length=20, choices=TX_STATUS, default='pending', verbose_name=_("状态"))
timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_("时间"))
class Meta:
verbose_name = _("充提记录")
verbose_name_plural = _("充提管理")

View File

@ -1,25 +1,198 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<html lang="zh-hans">
<head>
<meta charset="UTF-8">
<title>{% block title %}Knowledge Base{% endblock %}</title>
{% if project_description %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ project_name }} - 全球领先的数字资产交易平台</title>
<meta name="description" content="{{ project_description }}">
<meta property="og:description" content="{{ project_description }}">
<meta property="twitter:description" content="{{ project_description }}">
{% endif %}
{% if project_image_url %}
<meta property="og:image" content="{{ project_image_url }}">
<meta property="twitter:image" content="{{ project_image_url }}">
{% endif %}
{% load static %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
{% block head %}{% endblock %}
<style>
:root {
--bg-dark: #0b0e11;
--bg-card: #181a20;
--text-primary: #eaecef;
--text-secondary: #848e9c;
--accent-color: #fcd535;
--up-color: #0ecb81;
--down-color: #f6465d;
}
body {
background-color: var(--bg-dark);
color: var(--text-primary);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 0;
}
.navbar {
background-color: var(--bg-dark);
border-bottom: 1px solid #2b2f36;
padding: 0.75rem 1.5rem;
}
.nav-link {
color: var(--text-primary) !important;
font-weight: 500;
font-size: 14px;
margin: 0 8px;
transition: color 0.2s;
}
.nav-link:hover { color: var(--accent-color) !important; }
.btn-primary {
background-color: var(--accent-color);
border-color: var(--accent-color);
color: #181a20;
font-weight: 600;
padding: 8px 20px;
border-radius: 4px;
}
.footer {
background-color: var(--bg-dark);
padding: 60px 0 30px 0;
border-top: 1px solid #2b2f36;
}
.footer h6 { color: #eaecef; margin-bottom: 20px; font-weight: 600; }
.footer ul li { margin-bottom: 12px; }
.footer ul li a { color: #848e9c; text-decoration: none; font-size: 14px; transition: color 0.2s; }
.footer ul li a:hover { color: var(--accent-color); }
.glass-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
}
/* Floating Customer Service Icon */
.cs-float {
position: fixed;
right: 30px;
bottom: 40px;
width: 60px;
height: 60px;
background: var(--accent-color);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #181a20;
font-size: 28px;
box-shadow: 0 4px 20px rgba(252, 213, 53, 0.4);
cursor: pointer;
z-index: 9999;
transition: all 0.3s;
text-decoration: none;
}
.cs-float:hover {
transform: scale(1.1) rotate(5deg);
color: #181a20;
}
</style>
</head>
<body>
{% block content %}{% endblock %}
</body>
<nav class="navbar navbar-expand-lg sticky-top">
<div class="container-fluid">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="{% static 'images/logo.png' %}" alt="Logo" style="height: 28px; margin-right: 8px;">
<span class="fw-bold fs-5 text-white">{{ project_name }}</span>
</a>
<button class="navbar-toggler border-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<i class="bi bi-list text-white"></i>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="{% url 'core:index' %}">首页</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'core:spot_trade' %}">现货交易</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'core:contract_trade' %}">合约交易</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'core:market_center' %}">行情中心</a></li>
</ul>
<div class="d-flex align-items-center">
{% if user.is_authenticated %}
<div class="dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
<i class="bi bi-person-circle fs-5 me-2"></i>
<span>{{ user.username }}</span>
</a>
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow">
<li><a class="dropdown-item" href="{% url 'core:profile' %}"><i class="bi bi-person-badge me-2"></i>个人中心</a></li>
<li><a class="dropdown-item" href="{% url 'core:profile' %}"><i class="bi bi-wallet2 me-2"></i>我的资产</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="/login/"><i class="bi bi-box-arrow-right me-2"></i>安全退出</a></li>
</ul>
</div>
{% else %}
<a href="{% url 'core:login' %}" class="nav-link me-3">登录</a>
<a href="{% url 'core:register' %}" class="btn btn-primary">立即注册</a>
{% endif %}
</div>
</div>
</div>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<!-- Floating CS -->
{% if site_settings.customer_service_url %}
<a href="{{ site_settings.customer_service_url }}" target="_blank" class="cs-float" title="在线客服">
<i class="bi bi-headset"></i>
</a>
{% endif %}
<footer class="footer">
<div class="container">
<div class="row g-4">
<div class="col-md-3">
<h5 class="fw-bold mb-4 text-white">{{ project_name }}</h5>
<p class="text-secondary small">全球领先的数字资产交易平台,致力于为用户提供安全、专业、透明的数字资产一站式服务。</p>
</div>
<div class="col-md-2 offset-md-1">
<h6>产品服务</h6>
<ul class="list-unstyled">
<li><a href="{% url 'core:spot_trade' %}">现货交易</a></li>
<li><a href="{% url 'core:contract_trade' %}">永续合约</a></li>
<li><a href="{% url 'core:placeholder' '币安赚币' %}">理财中心</a></li>
</ul>
</div>
<div class="col-md-2">
<h6>法律法规</h6>
<ul class="list-unstyled">
<li><a href="{% url 'core:placeholder' '服务条款' %}">服务条款</a></li>
<li><a href="{% url 'core:placeholder' '隐私政策' %}">隐私政策</a></li>
<li><a href="{% url 'core:placeholder' '免责声明' %}">免责声明</a></li>
</ul>
</div>
<div class="col-md-2">
<h6>客户支持</h6>
<ul class="list-unstyled">
<li><a href="{% url 'core:placeholder' '帮助中心' %}">帮助中心</a></li>
<li><a href="{% url 'core:placeholder' '公告中心' %}">公告中心</a></li>
<li><a href="{% url 'core:placeholder' '提交请求' %}">提交工单</a></li>
</ul>
</div>
<div class="col-md-2">
<h6>关于我们</h6>
<ul class="list-unstyled">
<li><a href="{% url 'core:placeholder' '公司简介' %}">公司简介</a></li>
<li><a href="{% url 'core:placeholder' '加入我们' %}">招贤纳士</a></li>
</ul>
</div>
</div>
<hr class="my-5" style="border-color: #2b2f36;">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-start">
<p class="text-secondary small mb-0">&copy; 2026 {{ project_name }} Digital Asset Exchange. All rights reserved.</p>
</div>
<div class="col-md-6 text-center text-md-end mt-3 mt-md-0">
<div class="text-secondary small">
<i class="bi bi-globe me-2"></i> 简体中文 | USDT
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -1,14 +1,66 @@
{% extends 'base.html' %}
{% block title %}{{ article.title }}{% endblock %}
{% load static %}
{% block content %}
<div class="container mt-5">
<h1>{{ article.title }}</h1>
<p class="text-muted">Published on {{ article.created_at|date:"F d, Y" }}</p>
<hr>
<div>
{{ article.content|safe }}
<div class="container py-5">
<div class="row">
<div class="col-lg-8 offset-lg-2">
<div class="glass-card p-5">
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/" class="text-warning text-decoration-none">首页</a></li>
<li class="breadcrumb-item active text-secondary" aria-current="page">{{ title }}</li>
</ol>
</nav>
<h1 class="fw-bold mb-4 text-white">{{ title }}</h1>
<div class="content text-secondary lh-lg">
<p class="lead text-white mb-5">{{ content }}</p>
{% if faqs %}
<hr class="my-5 border-secondary">
<h3 class="text-white mb-4">常见问题 (FAQ)</h3>
<div class="accordion accordion-flush bg-transparent" id="faqAccordion">
{% for faq in faqs %}
<div class="accordion-item bg-transparent border-secondary">
<h2 class="accordion-header">
<button class="accordion-button bg-transparent text-white collapsed border-0 py-3" type="button" data-bs-toggle="collapse" data-bs-target="#q{{ forloop.counter }}">
{{ faq.q }}
</button>
</h2>
<div id="q{{ forloop.counter }}" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body text-secondary pb-4">
{{ faq.a }}
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div class="mt-5 p-4 bg-dark bg-opacity-50 rounded border border-secondary text-center">
<h5 class="text-white mb-3">还需要更多帮助吗?</h5>
<p class="small text-secondary">我们的全球客服团队 24/7 全天候在线为您提供专业支持。</p>
<div class="d-flex justify-content-center gap-3 mt-4">
<button class="btn btn-warning px-4 fw-bold">联系在线客服</button>
<button class="btn btn-outline-light px-4">查阅文档</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.accordion-button:after {
filter: invert(1);
}
.accordion-button:not(.collapsed) {
background-color: transparent;
box-shadow: none;
color: var(--accent-color);
}
</style>
{% endblock %}

View File

@ -0,0 +1,65 @@
{% extends "base.html" %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-6 animate-up">
<div class="glass-card p-4">
<h3 class="fw-bold mb-4 text-center">充值数字资产</h3>
<div class="mb-4">
<label class="form-label text-secondary">选择币种</label>
<div class="d-flex align-items-center bg-dark p-3 rounded border border-secondary border-opacity-25">
<i class="bi bi-coin text-warning fs-4 me-3"></i>
<div class="flex-grow-1">
<h6 class="m-0 fw-bold">USDT</h6>
<small class="text-secondary">Tether US</small>
</div>
<i class="bi bi-chevron-down text-secondary"></i>
</div>
</div>
<div class="mb-4">
<label class="form-label text-secondary">选择网络</label>
<div class="row g-2">
<div class="col-4"><button class="btn btn-sm btn-outline-warning w-100 active">TRC20</button></div>
<div class="col-4"><button class="btn btn-sm btn-outline-secondary w-100">ERC20</button></div>
<div class="col-4"><button class="btn btn-sm btn-outline-secondary w-100">BEP20</button></div>
</div>
</div>
<div class="bg-black bg-opacity-50 p-4 rounded-3 text-center mb-4 border border-secondary border-opacity-10">
<div class="mb-3">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=TRX789sDqW12vNpkL90ZxY56uMvB45RtQp" alt="QR" class="img-fluid rounded">
</div>
<small class="text-secondary d-block mb-2">充值地址 (TRC20)</small>
<div class="d-flex align-items-center justify-content-center gap-2">
<span class="fw-bold text-break small" style="color: #f0b90b;">TRX789sDqW12vNpkL90ZxY56uMvB45RtQp</span>
<i class="bi bi-files text-secondary cursor-pointer" onclick="alert('地址已复制')"></i>
</div>
</div>
<div class="alert alert-warning border-0 bg-warning bg-opacity-10 text-warning-emphasis small mb-4">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
请仅向该地址充值 USDT。任何其他资产的充值都将无法找回。
</div>
<form method="POST">
{% csrf_token %}
<div class="mb-3">
<label class="form-label text-secondary">充值金额</label>
<div class="input-group">
<input type="number" name="amount" step="0.01" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="0.00" required>
<span class="input-group-text bg-dark text-white border-secondary border-opacity-25">USDT</span>
</div>
</div>
<div class="mb-4">
<label class="form-label text-secondary">交易哈希 (TXID)</label>
<input type="text" name="tx_id" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="请输入链上交易ID" required>
</div>
<button type="submit" class="btn btn-primary-custom w-100 py-3">提交充值确认</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,145 +1,235 @@
{% extends "base.html" %}
{% extends 'base.html' %}
{% load static %}
{% block title %}{{ project_name }}{% endblock %}
{% block content %}
<!-- Hero Carousel Section -->
<div id="heroCarousel" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-indicators">
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="0" class="active"></button>
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="1"></button>
<button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="2"></button>
</div>
<div class="carousel-inner">
<!-- Slide 1: Welcome -->
<div class="carousel-item active" style="height: 550px; background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('https://images.unsplash.com/photo-1621761191319-c6fb62004040?q=80&w=2070&auto=format&fit=crop'); background-size: cover; background-position: center;">
<div class="container h-100 d-flex align-items-center">
<div class="row w-100 align-items-center">
<div class="col-lg-7">
<h1 class="display-3 fw-bold mb-4">开启您的<br><span style="color: var(--accent-color);">加密货币</span>之旅</h1>
<p class="lead text-light mb-5">在全球最受信任的交易平台买卖和存储加密货币。BitCrypto 为您提供安全、稳定、高效的服务。</p>
<div class="d-flex gap-3">
<a href="{% url 'core:register' %}" class="btn btn-warning btn-lg px-5 fw-bold">立即注册</a>
<a href="{% url 'core:spot_trade' %}" class="btn btn-outline-light btn-lg px-5 fw-bold">开始交易</a>
</div>
</div>
</div>
</div>
</div>
<!-- Slide 2: Derivatives -->
<div class="carousel-item" style="height: 550px; background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('https://images.unsplash.com/photo-1639762681485-074b7f938ba0?q=80&w=2064&auto=format&fit=crop'); background-size: cover; background-position: center;">
<div class="container h-100 d-flex align-items-center">
<div class="row w-100 align-items-center justify-content-end">
<div class="col-lg-7 text-lg-end">
<h1 class="display-4 fw-bold mb-4">领先的<span style="color: var(--accent-color);">衍生品</span>交易平台</h1>
<p class="lead text-light mb-5">最高200倍杠杆支持多种永续合约毫秒级撮合引擎。多空双向交易灵活捕捉市场机会。</p>
<a href="{% url 'core:contract_trade' %}" class="btn btn-warning btn-lg px-5 fw-bold">进入合约交易</a>
</div>
</div>
</div>
</div>
<!-- Slide 3: App Download -->
<div class="carousel-item" style="height: 550px; background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('https://images.unsplash.com/photo-1622639225985-84f8877b0c30?q=80&w=1944&auto=format&fit=crop'); background-size: cover; background-position: center;">
<div class="container h-100 d-flex align-items-center">
<div class="row w-100 align-items-center">
<div class="col-lg-12 text-center">
<h1 class="display-4 fw-bold mb-4">随时随地,随心交易</h1>
<p class="lead text-light mb-5">下载 BitCrypto App享受极致交易体验。专业图表、实时推送、资产管理尽在掌握。</p>
<div class="d-flex justify-content-center gap-5">
<div class="download-badge">
<i class="bi bi-apple fs-1 mb-2"></i>
<p class="small mb-0">App Store</p>
</div>
<div class="download-badge">
<i class="bi bi-android2 fs-1 mb-2"></i>
<p class="small mb-0">Android APK</p>
</div>
<div class="download-badge">
<i class="bi bi-qr-code fs-1 mb-2"></i>
<p class="small mb-0">扫码下载</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#heroCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon"></span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#heroCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon"></span>
</button>
</div>
<!-- Quick Actions Section -->
<div class="container" style="margin-top: -50px; position: relative; z-index: 10;">
<div class="glass-card p-4 d-flex flex-wrap justify-content-around align-items-center text-center shadow-lg" style="border-radius: 12px; border: 1px solid rgba(255,193,7,0.3);">
<div class="download-item mb-3 mb-md-0">
<h6 class="text-secondary mb-2 small fw-bold">iOS 下载</h6>
<button class="btn btn-outline-light border-secondary px-4"><i class="bi bi-apple me-2 text-warning"></i>App Store</button>
</div>
<div class="vr d-none d-md-block opacity-25"></div>
<div class="download-item mb-3 mb-md-0">
<h6 class="text-secondary mb-2 small fw-bold">安卓下载</h6>
<button class="btn btn-outline-light border-secondary px-4"><i class="bi bi-android2 me-2 text-warning"></i>Android</button>
</div>
<div class="vr d-none d-md-block opacity-25"></div>
<div class="download-item">
<h6 class="text-secondary mb-2 small fw-bold">API 支持</h6>
<button class="btn btn-outline-light border-secondary px-4"><i class="bi bi-code-square me-2 text-warning"></i>开发者文档</button>
</div>
</div>
</div>
<!-- Market Section -->
<section id="markets" class="container py-5 mt-4">
<div class="d-flex justify-content-between align-items-end mb-4">
<div>
<h2 class="fw-bold">热门行情</h2>
<p class="text-secondary mb-0">实时获取全球顶级加密货币价格走势</p>
</div>
<a href="{% url 'core:market_center' %}" class="text-warning text-decoration-none fw-bold">查看更多市场 <i class="bi bi-arrow-right"></i></a>
</div>
<div class="table-responsive">
<table class="table table-dark table-hover align-middle" style="border-radius: 12px; overflow: hidden; border: 1px solid #2b3139;">
<thead class="text-secondary">
<tr style="background: #1e2329;">
<th scope="col" class="ps-4 py-3">币种</th>
<th scope="col" class="py-3">价格</th>
<th scope="col" class="py-3">24h 涨跌</th>
<th scope="col" class="py-3">24h 最高 / 最低</th>
<th scope="col" class="text-end pe-4 py-3">操作</th>
</tr>
</thead>
<tbody id="market-list">
<tr>
<td colspan="5" class="text-center py-5">
<div class="spinner-border text-warning" role="status"></div>
<p class="mt-2 text-secondary">正在加载实时行情...</p>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Features -->
<section class="container py-5">
<h2 class="text-center fw-bold mb-5">领先行业的资产安全防护</h2>
<div class="row g-4 text-center">
<div class="col-md-4">
<div class="p-5 h-100 glass-card">
<div class="icon-box mb-4 mx-auto" style="width: 80px; height: 80px; background: rgba(255,193,7,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="bi bi-shield-check display-5 text-warning"></i>
</div>
<h4 class="fw-bold mb-3">资产保障基金</h4>
<p class="text-secondary">BitCrypto 每日提取 10% 的交易手续费,存入资产保障基金,为您在极端情况下的资产提供全额赔付保障。</p>
</div>
</div>
<div class="col-md-4">
<div class="p-5 h-100 glass-card">
<div class="icon-box mb-4 mx-auto" style="width: 80px; height: 80px; background: rgba(255,193,7,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="bi bi-cpu display-5 text-warning"></i>
</div>
<h4 class="fw-bold mb-3">高性能交易引擎</h4>
<p class="text-secondary">每秒 200 万次的交易撮合能力,保证在市场波动剧烈时依然能够快速响应,不漏掉任何一个成交机会。</p>
</div>
</div>
<div class="col-md-4">
<div class="p-5 h-100 glass-card">
<div class="icon-box mb-4 mx-auto" style="width: 80px; height: 80px; background: rgba(255,193,7,0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="bi bi-safe2 display-5 text-warning"></i>
</div>
<h4 class="fw-bold mb-3">严密的隐私保护</h4>
<p class="text-secondary">我们对用户数据采取离线分布式存储和多层加密,绝不将个人隐私泄露给任何第三方,确保您的身份隐私。</p>
</div>
</div>
</div>
</section>
{% block head %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
.download-badge {
transition: transform 0.3s;
cursor: pointer;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 100%;
}
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2.5rem 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
}
h1 {
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
font-weight: 700;
margin: 0 0 1.2rem;
letter-spacing: -0.02em;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
opacity: 0.92;
}
.loader {
margin: 1.5rem auto;
width: 56px;
height: 56px;
border: 4px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.runtime code {
background: rgba(0, 0, 0, 0.25);
padding: 0.15rem 0.45rem;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
footer {
position: absolute;
bottom: 1rem;
width: 100%;
text-align: center;
font-size: 0.85rem;
opacity: 0.75;
.download-badge:hover {
transform: translateY(-5px);
color: var(--accent-color);
}
.icon-box { transition: all 0.3s; }
.glass-card:hover .icon-box { transform: scale(1.1); background: rgba(255,193,7,0.2) !important; }
.coin-icon { width: 32px; height: 32px; margin-right: 12px; }
</style>
{% endblock %}
{% block content %}
<main>
<div class="card">
<h1>Analyzing your requirements and generating your app…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
{% block scripts %}
<script>
const symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'ADAUSDT', 'XRPUSDT', 'DOTUSDT', 'DOGEUSDT', 'MATICUSDT', 'LINKUSDT'];
const coinIcons = {
'BTC': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1.png',
'ETH': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1027.png',
'BNB': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1839.png',
'SOL': 'https://s2.coinmarketcap.com/static/img/coins/64x64/5426.png',
'ADA': 'https://s2.coinmarketcap.com/static/img/coins/64x64/2010.png',
'XRP': 'https://s2.coinmarketcap.com/static/img/coins/64x64/52.png',
'DOT': 'https://s2.coinmarketcap.com/static/img/coins/64x64/6636.png',
'DOGE': 'https://s2.coinmarketcap.com/static/img/coins/64x64/74.png',
'MATIC': 'https://s2.coinmarketcap.com/static/img/coins/64x64/3890.png',
'LINK': 'https://s2.coinmarketcap.com/static/img/coins/64x64/1975.png'
};
async function fetchMarkets() {
try {
const response = await fetch('https://api.binance.com/api/v3/ticker/24hr?symbols=' + JSON.stringify(symbols));
const data = await response.json();
const list = document.getElementById('market-list');
list.innerHTML = '';
data.forEach(coin => {
const symbolBase = coin.symbol.replace('USDT', '');
const change = parseFloat(coin.priceChangePercent);
const changeClass = change >= 0 ? 'text-success' : 'text-danger';
const changeIcon = change >= 0 ? '+' : '';
const iconUrl = coinIcons[symbolBase] || 'https://s2.coinmarketcap.com/static/img/coins/64x64/1.png';
list.innerHTML += `
<tr>
<td class="ps-4 py-3">
<div class="d-flex align-items-center">
<img src="${iconUrl}" class="coin-icon" alt="${symbolBase}">
<div>
<span class="fw-bold text-white">${symbolBase}</span>
<span class="text-secondary small ms-1">/ USDT</span>
</div>
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
<p class="runtime">
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
</p>
</div>
</main>
<footer>
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
</footer>
</td>
<td class="fw-bold py-3 text-white">$${parseFloat(coin.lastPrice).toLocaleString()}</td>
<td class="${changeClass} py-3 fw-bold">${changeIcon}${change.toFixed(2)}%</td>
<td class="py-3">
<div class="small text-white">高: $${parseFloat(coin.highPrice).toLocaleString()}</div>
<div class="small text-secondary">低: $${parseFloat(coin.lowPrice).toLocaleString()}</div>
</td>
<td class="text-end pe-4 py-3">
<a href="/trade/spot/?symbol=${coin.symbol}" class="btn btn-sm btn-warning fw-bold px-3 shadow-sm">交易</a>
</td>
</tr>
`;
});
} catch (error) { console.error('Error:', error); }
}
fetchMarkets();
setInterval(fetchMarkets, 5000);
</script>
{% endblock %}

View File

@ -0,0 +1,86 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%);">
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 450px; border-radius: 16px;">
<div class="text-center mb-5">
<div class="logo-circle mb-4 mx-auto">
<i class="bi bi-hexagon-fill text-warning display-4"></i>
</div>
<h2 class="fw-bold text-white">欢迎登录 BitCrypto</h2>
<p class="text-secondary">全球领先的加密资产交易平台</p>
</div>
<form method="post">
{% csrf_token %}
{% for field in form %}
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">{{ field.label }}</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
{% if '用户' in field.label %}<i class="bi bi-person"></i>{% else %}<i class="bi bi-lock"></i>{% endif %}
</span>
<input type="{{ field.field.widget.input_type }}" name="{{ field.name }}" class="form-control bg-transparent text-white border-secondary" placeholder="请输入{{ field.label }}" required>
</div>
{% for error in field.errors %}
<div class="text-danger x-small mt-1">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-flex justify-content-between align-items-center mb-4">
<div class="form-check">
<input class="form-check-input bg-transparent border-secondary" type="checkbox" id="remember">
<label class="form-check-label text-secondary small" for="remember">自动登录</label>
</div>
<a href="#" class="text-warning small text-decoration-none">忘记密码?</a>
</div>
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">登录</button>
</form>
<div class="text-center">
<p class="text-secondary small">还没有账户? <a href="{% url 'core:register' %}" class="text-warning text-decoration-none fw-bold">立即注册</a></p>
</div>
<div class="mt-5 pt-4 border-top border-secondary text-center">
<p class="text-secondary x-small mb-3">其他登录方式</p>
<div class="d-flex justify-content-center gap-4">
<a href="#" class="text-secondary fs-4"><i class="bi bi-google"></i></a>
<a href="#" class="text-secondary fs-4"><i class="bi bi-apple"></i></a>
<a href="#" class="text-secondary fs-4"><i class="bi bi-qr-code-scan"></i></a>
</div>
</div>
</div>
</div>
<style>
.auth-card {
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
}
.input-group-text {
border-right: none;
}
input.form-control {
border-left: none;
padding: 12px;
}
input.form-control:focus {
background: rgba(255, 255, 255, 0.05) !important;
box-shadow: none;
border-color: var(--accent-color);
}
.x-small { font-size: 11px; }
.logo-circle {
width: 80px;
height: 80px;
background: rgba(240, 185, 11, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
</style>
{% endblock %}

View File

@ -0,0 +1,107 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold text-white mb-0">行情中心</h2>
<div class="input-group w-auto">
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
<input type="text" id="market-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterMarket()">
</div>
</div>
<div class="glass-card overflow-hidden" style="border-radius: 12px; border: 1px solid #2b3139; background: #161a1e;">
<div class="table-responsive">
<table class="table table-dark table-hover mb-0 align-middle">
<thead>
<tr class="text-secondary border-bottom border-secondary">
<th class="ps-4 py-3">币种</th>
<th class="py-3">最新价</th>
<th class="py-3">24h 涨跌</th>
<th class="py-3">24h 最高</th>
<th class="py-3">24h 最低</th>
<th class="py-3">24h 成交额</th>
<th class="pe-4 py-3 text-end">操作</th>
</tr>
</thead>
<tbody id="market-table-body">
{% for crypto in cryptos %}
<tr class="market-row" data-symbol="{{ crypto.symbol }}">
<td class="ps-4 py-3">
<div class="d-flex align-items-center">
<img src="https://static.okx.com/cdn/oksupport/asset/currency/icon/{{ crypto.symbol|lower }}.png"
class="rounded-circle me-3" width="32" height="32"
onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">
<div>
<div class="fw-bold text-white">{{ crypto.symbol }}</div>
<div class="text-secondary small">{{ crypto.name }}</div>
</div>
</div>
</td>
<td class="py-3 fw-bold price-val">--</td>
<td class="py-3 change-val">--</td>
<td class="py-3 high-val text-secondary">--</td>
<td class="py-3 low-val text-secondary">--</td>
<td class="py-3 vol-val text-secondary">--</td>
<td class="pe-4 py-3 text-end">
<a href="{% url 'core:trade' 'spot' %}?symbol={{ crypto.symbol }}USDT" class="btn btn-outline-warning btn-sm px-3 me-2">现货</a>
<a href="{% url 'core:trade' 'contract' %}?symbol={{ crypto.symbol }}USDT" class="btn btn-warning btn-sm px-3 text-dark">合约</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<style>
.glass-card { background: rgba(22, 26, 30, 0.8); backdrop-filter: blur(10px); }
.table-dark { --bs-table-bg: transparent; }
.table-hover tbody tr:hover { background-color: rgba(255, 255, 255, 0.05) !important; }
</style>
{% endblock %}
{% block scripts %}
<script>
async function updateMarket() {
try {
const r = await fetch('https://api.binance.com/api/v3/ticker/24hr');
const data = await r.json();
const tickers = {};
data.forEach(t => {
if (t.symbol.endsWith('USDT')) {
tickers[t.symbol.replace('USDT', '')] = t;
}
});
document.querySelectorAll('.market-row').forEach(row => {
const sym = row.getAttribute('data-symbol');
const t = tickers[sym];
if (t) {
const chg = parseFloat(t.priceChangePercent);
row.querySelector('.price-val').textContent = parseFloat(t.lastPrice).toLocaleString();
row.querySelector('.price-val').className = 'py-3 fw-bold price-val ' + (chg >= 0 ? 'text-success' : 'text-danger');
row.querySelector('.change-val').textContent = (chg >= 0 ? '+' : '') + chg.toFixed(2) + '%';
row.querySelector('.change-val').className = 'py-3 change-val ' + (chg >= 0 ? 'text-success' : 'text-danger');
row.querySelector('.high-val').textContent = parseFloat(t.highPrice).toLocaleString();
row.querySelector('.low-val').textContent = parseFloat(t.lowPrice).toLocaleString();
row.querySelector('.vol-val').textContent = (parseFloat(t.quoteVolume) / 1000000).toFixed(2) + 'M';
}
});
} catch(e) {}
}
function filterMarket() {
const query = document.getElementById('market-search').value.toLowerCase();
document.querySelectorAll('.market-row').forEach(row => {
const sym = row.getAttribute('data-symbol').toLowerCase();
row.style.display = sym.includes(query) ? '' : 'none';
});
}
updateMarket();
setInterval(updateMarket, 3000);
</script>
{% endblock %}

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>{{ site_settings.site_name|default:"BitCrypto" }} - 移动版</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<style>
body { background-color: #0b0e11; color: #eaecef; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; overflow-x: hidden; }
.mobile-nav { position: fixed; bottom: 0; left: 0; right: 0; background: #161a1e; display: flex; justify-content: space-around; padding: 10px 0; border-top: 1px solid #2b3139; z-index: 1000; }
.nav-item { text-align: center; color: #848e9c; text-decoration: none; font-size: 10px; }
.nav-item.active { color: #f0b90b; }
.nav-item i { font-size: 20px; display: block; margin-bottom: 2px; }
.glass-card { background: #161a1e; border: 1px solid #2b3139; border-radius: 8px; margin-bottom: 10px; }
.x-small { font-size: 11px; }
.text-warning { color: #f0b90b !important; }
.btn-warning { background-color: #f0b90b; border-color: #f0b90b; color: #000; }
.form-control, .input-group-text { background-color: #1e2329 !important; border-color: #2b3139 !important; color: white !important; }
.custom-range::-webkit-slider-thumb { background: #f0b90b; cursor: pointer; height: 16px; width: 16px; }
.custom-range-danger::-webkit-slider-thumb { background: #f6465d; cursor: pointer; height: 16px; width: 16px; }
body { padding-bottom: 70px; }
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
<div class="p-2">
{% block content %}{% endblock %}
</div>
<div class="mobile-nav">
<a href="{% url 'core:index' %}" class="nav-item {% if request.resolver_match.url_name == 'index' %}active{% endif %}">
<i class="bi bi-house-door"></i>首页
</a>
<a href="{% url 'core:market_center' %}" class="nav-item {% if request.resolver_match.url_name == 'market_center' %}active{% endif %}">
<i class="bi bi-graph-up"></i>行情
</a>
<a href="{% url 'core:trade' 'spot' %}" class="nav-item {% if 'trade' in request.resolver_match.url_name %}active{% endif %}">
<i class="bi bi-arrow-left-right"></i>交易
</a>
<a href="{% url 'core:profile' %}" class="nav-item {% if request.resolver_match.url_name == 'profile' %}active{% endif %}">
<i class="bi bi-person"></i>我的
</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,44 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="p-3">
<div class="mb-4">
<a href="{% url 'core:profile' %}" class="text-decoration-none text-secondary small"><i class="bi bi-chevron-left"></i> 返回</a>
<h4 class="fw-bold mt-2">充值 USDT</h4>
</div>
<div class="glass-card p-3 mb-3">
<div class="text-secondary x-small mb-2">选择币种</div>
<div class="bg-dark p-2 rounded d-flex justify-content-between align-items-center mb-3">
<span class="fw-bold">USDT</span>
<span class="badge bg-warning text-dark">TRC20</span>
</div>
<div class="text-center py-4 bg-white rounded mb-3">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=TFA7S7S7S7S7S7S7S7S7S7S7S7S7S7" alt="QR" style="width: 120px;">
</div>
<div class="text-secondary x-small mb-1">充值地址</div>
<div class="input-group input-group-sm mb-3">
<input type="text" class="form-control" value="TFA7S7S7S7S7S7S7S7S7S7S7S7S7S7" readonly>
<button class="btn btn-warning" onclick="alert('已复制')">复制</button>
</div>
<div class="alert alert-dark x-small py-2">
<i class="bi bi-info-circle me-1"></i> 请务必确认网络为 TRC20否则资产将无法找回。
</div>
</div>
<form method="POST" class="glass-card p-3">
{% csrf_token %}
<div class="mb-3">
<label class="form-label small text-secondary">充值金额</label>
<input type="number" name="amount" class="form-control" placeholder="请输入充值金额" required>
</div>
<div class="mb-3">
<label class="form-label small text-secondary">交易哈希 (TXID)</label>
<input type="text" name="tx_id" class="form-control" placeholder="请输入流水号" required>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold">提交充值申请</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,88 @@
{% extends 'core/mobile/base_mobile.html' %}
{% load static %}
{% block content %}
<div class="glass-card p-3 text-center mb-3" style="background: linear-gradient(135deg, #161a1e 0%, #1e2329 100%);">
<img src="{% static 'images/logo.png' %}" alt="Logo" style="height: 40px;" class="mb-2">
<h4 class="fw-bold">全球领先的数字资产交易平台</h4>
<p class="text-secondary small">安全、稳定、快捷的加密货币交易体验</p>
{% if not user.is_authenticated %}
<div class="d-grid gap-2">
<a href="{% url 'core:register' %}" class="btn btn-warning fw-bold">立即注册</a>
<a href="{% url 'core:login' %}" class="btn btn-outline-secondary btn-sm text-white">登录已有账户</a>
</div>
{% else %}
<div class="d-flex justify-content-around mt-2">
<div>
<div class="text-secondary x-small">账户总资产 (USDT)</div>
<div class="fs-5 fw-bold">{{ account.balance|add:account.frozen_balance }}</div>
</div>
</div>
{% endif %}
</div>
<div class="row g-2 mb-3">
<div class="col-4">
<a href="{% url 'core:deposit' %}" class="glass-card p-2 d-block text-center text-decoration-none text-white">
<i class="bi bi-wallet2 fs-4 text-warning"></i>
<div class="x-small mt-1">充币</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'core:withdraw' %}" class="glass-card p-2 d-block text-center text-decoration-none text-white">
<i class="bi bi-cash-stack fs-4 text-info"></i>
<div class="x-small mt-1">提币</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'core:verify' %}" class="glass-card p-2 d-block text-center text-decoration-none text-white">
<i class="bi bi-shield-check fs-4 text-success"></i>
<div class="x-small mt-1">认证</div>
</a>
</div>
</div>
<div class="glass-card p-2">
<div class="d-flex justify-content-between align-items-center mb-2 px-1">
<span class="fw-bold small">热门行情</span>
<a href="{% url 'core:market_center' %}" class="text-warning x-small text-decoration-none">更多 <i class="bi bi-chevron-right"></i></a>
</div>
<div id="hot-list">
<!-- JS populated -->
</div>
</div>
<style>
.hot-item { display: flex; justify-content: space-between; align-items: center; padding: 12px 8px; border-bottom: 1px solid #2b3139; }
.hot-item:last-child { border-bottom: 0; }
</style>
{% endblock %}
{% block scripts %}
<script>
async function getHot() {
try {
const r = await fetch('/api/market_data/');
const data = await r.json();
const list = document.getElementById('hot-list');
list.innerHTML = '';
data.slice(0, 8).forEach(c => {
const base = c.symbol.replace('USDT', '');
list.innerHTML += `
<div class="hot-item" onclick="location.href='{% url 'core:trade' 'spot' %}?symbol=${c.symbol}'">
<div>
<div class="fw-bold">${base}<span class="text-secondary x-small">/USDT</span></div>
<div class="text-secondary x-small">Vol ${ (Math.random()*100).toFixed(2) }M</div>
</div>
<div class="text-end">
<div class="fw-bold">${c.price}</div>
<div class="${c.change>=0?'text-success':'text-danger'} x-small">${c.change>=0?'+':''}${c.change}%</div>
</div>
</div>`;
});
} catch(e) {}
}
getHot();
setInterval(getHot, 5000);
</script>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="p-3">
<div class="text-center mb-4 mt-5">
<h3 class="fw-bold">欢迎回来</h3>
<p class="text-secondary small">登录您的 {{ site_settings.site_name|default:"BitCrypto" }} 账户</p>
</div>
<form method="POST" class="glass-card p-4">
{% csrf_token %}
<div class="mb-3">
<label class="form-label small text-secondary">用户名</label>
<input type="text" name="username" class="form-control" required placeholder="请输入用户名">
</div>
<div class="mb-4">
<label class="form-label small text-secondary">登录密码</label>
<input type="password" name="password" class="form-control" required placeholder="请输入密码">
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 mb-3">立即登录</button>
<div class="text-center small">
<span class="text-secondary">还没有账户?</span>
<a href="{% url 'core:register' %}" class="text-warning text-decoration-none">立即注册</a>
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,63 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="glass-card p-2 mb-2">
<div class="input-group input-group-sm">
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
<input type="text" id="market-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterMarkets()">
</div>
</div>
<div class="glass-card p-0">
<div class="d-flex justify-content-between p-2 text-secondary x-small border-bottom border-secondary">
<span style="width: 40%;">名称</span>
<span style="width: 30%;" class="text-end">最新价</span>
<span style="width: 30%;" class="text-end">24h 涨跌</span>
</div>
<div id="market-list">
<!-- JS populated -->
</div>
</div>
<style>
.market-row { display: flex; justify-content: space-between; align-items: center; padding: 12px 8px; border-bottom: 1px solid #2b3139; }
</style>
{% endblock %}
{% block scripts %}
<script>
let allMarkets = [];
async function getMarkets() {
try {
const r = await fetch('/api/market_data/');
allMarkets = await r.json();
renderMarkets();
} catch(e) {}
}
function renderMarkets(filter = '') {
const list = document.getElementById('market-list');
list.innerHTML = '';
allMarkets.forEach(c => {
if (filter && !c.symbol.toLowerCase().includes(filter.toLowerCase())) return;
const base = c.symbol.replace('USDT', '');
list.innerHTML += `
<div class="market-row" onclick="location.href='{% url 'core:trade' 'spot' %}?symbol=${c.symbol}'">
<div style="width: 40%;">
<div class="fw-bold">${base}<span class="text-secondary x-small">/USDT</span></div>
</div>
<div style="width: 30%;" class="text-end fw-bold">${c.price}</div>
<div style="width: 30%;" class="text-end">
<span class="badge ${c.change>=0?'bg-success':'bg-danger'} py-1" style="min-width: 60px;">${c.change>=0?'+':''}${c.change}%</span>
</div>
</div>`;
});
}
function filterMarkets() {
renderMarkets(document.getElementById('market-search').value);
}
getMarkets();
setInterval(getMarkets, 3000);
</script>
{% endblock %}

View File

@ -0,0 +1,83 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="glass-card p-3 mb-3 d-flex align-items-center">
<div class="bg-warning rounded-circle d-flex align-items-center justify-content-center text-dark fw-bold me-3" style="width: 50px; height: 50px; font-size: 20px;">
{{ user.username|first|upper }}
</div>
<div>
<div class="fw-bold">{{ user.username }}</div>
<div class="text-secondary x-small">UID: {{ account.uid }} | {{ account.get_account_type_display }}</div>
</div>
<div class="ms-auto">
<span class="badge {% if account.kyc_status == 'VERIFIED' %}bg-success{% else %}bg-secondary{% endif %} x-small">
{{ account.get_kyc_status_display }}
</span>
</div>
</div>
<div class="glass-card p-3 mb-3">
<div class="text-secondary x-small mb-1">总资产估值 (USDT)</div>
<div class="fs-3 fw-bold mb-3">{{ account.balance|add:account.frozen_balance }}</div>
<div class="row g-2">
<div class="col-6">
<div class="bg-dark p-2 rounded">
<div class="text-secondary x-small">可用</div>
<div class="fw-bold small">{{ account.balance }}</div>
</div>
</div>
<div class="col-6">
<div class="bg-dark p-2 rounded">
<div class="text-secondary x-small">冻结</div>
<div class="fw-bold small">{{ account.frozen_balance }}</div>
</div>
</div>
</div>
</div>
<div class="glass-card mb-3">
<div class="list-group list-group-flush">
<a href="{% url 'core:deposit' %}" class="list-group-item list-group-item-action bg-transparent text-white border-secondary py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-wallet2 me-3 text-warning"></i>充币</span>
<i class="bi bi-chevron-right text-secondary"></i>
</a>
<a href="{% url 'core:withdraw' %}" class="list-group-item list-group-item-action bg-transparent text-white border-secondary py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-cash-stack me-3 text-info"></i>提币</span>
<i class="bi bi-chevron-right text-secondary"></i>
</a>
<a href="{% url 'core:verify' %}" class="list-group-item list-group-item-action bg-transparent text-white border-secondary py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-shield-check me-3 text-success"></i>实名认证</span>
<i class="bi bi-chevron-right text-secondary"></i>
</a>
</div>
</div>
<div class="glass-card p-3">
<div class="fw-bold small mb-2">资产明细</div>
<table class="table table-dark table-sm x-small mb-0">
<thead>
<tr class="text-secondary">
<th>币种</th>
<th class="text-end">可用</th>
<th class="text-end">冻结</th>
</tr>
</thead>
<tbody>
{% for asset in account.assets.all %}
<tr>
<td>{{ asset.currency }}</td>
<td class="text-end">{{ asset.balance }}</td>
<td class="text-end">{{ asset.frozen }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="mt-4 p-2">
<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-outline-danger w-100 btn-sm">退出登录</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,45 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="p-3">
<div class="text-center mb-4 mt-4">
<h3 class="fw-bold">注册账户</h3>
<p class="text-secondary small">加入 {{ site_settings.site_name|default:"BitCrypto" }},开启您的交易之旅</p>
</div>
{% if messages %}
<div class="mb-3">
{% for message in messages %}
<div class="alert alert-danger py-2 small">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
<form method="POST" class="glass-card p-4">
{% csrf_token %}
<div class="mb-3">
<label class="form-label small text-secondary">用户名 / 手机号</label>
<input type="text" name="username" class="form-control" required placeholder="请输入用户名">
</div>
<div class="mb-3">
<label class="form-label small text-secondary">登录密码</label>
<input type="password" name="password" class="form-control" required placeholder="请输入密码">
</div>
<div class="mb-3">
<label class="form-label small text-secondary">确认密码</label>
<input type="password" name="password_confirm" class="form-control" required placeholder="请再次输入密码">
</div>
<div class="mb-4">
<label class="form-label small text-secondary">图形验证码</label>
<div class="input-group">
<input type="text" name="captcha_input" class="form-control" required placeholder="结果">
<span class="input-group-text bg-dark text-warning fw-bold">{{ captcha_text }}</span>
</div>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 mb-3">立即注册</button>
<div class="text-center small">
<span class="text-secondary">已有账户?</span>
<a href="{% url 'core:login' %}" class="text-warning text-decoration-none">立即登录</a>
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,376 @@
{% extends 'core/mobile/base_mobile.html' %}
{% load static %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-2 px-1 py-1" style="background: #161a1e;">
<div class="dropdown">
<button class="btn btn-sm btn-dark dropdown-toggle fs-6 fw-bold text-warning" type="button" data-bs-toggle="dropdown">
<span id="current-symbol-display">{{ symbol }}</span>
</button>
<ul class="dropdown-menu dropdown-menu-dark shadow-lg" id="mobile-coin-list" style="max-height: 300px; overflow-y: auto;">
<!-- JS populated -->
</ul>
</div>
<div class="d-flex gap-1 bg-dark p-1 rounded">
<a href="{% url 'core:trade' 'spot' %}?symbol={{ symbol }}" class="btn btn-xs {% if trade_type == 'SPOT' %}btn-warning text-dark{% else %}btn-dark text-secondary{% endif %} py-1 px-3 small fw-bold">现货</a>
<a href="{% url 'core:trade' 'contract' %}?symbol={{ symbol }}" class="btn btn-xs {% if trade_type == 'CONTRACT' %}btn-warning text-dark{% else %}btn-dark text-secondary{% endif %} py-1 px-3 small fw-bold">合约</a>
</div>
</div>
<div class="row g-1 mb-2">
<!-- Market Data Column -->
<div class="col-5">
<div class="glass-card p-2" style="height: 100%; font-size: 11px; background: #161a1e; border: 1px solid #2b3139;">
<div class="d-flex justify-content-between text-secondary mb-2">
<span>价格</span><span>数量</span>
</div>
<div id="m-asks" class="mb-2"></div>
<div class="text-center py-2 fw-bold text-success border-top border-bottom border-secondary my-2" id="m-current-price" style="font-size: 16px;">--</div>
<div id="m-bids"></div>
</div>
</div>
<!-- Order Form Column -->
<div class="col-7">
<div class="glass-card p-2" style="background: #161a1e; border: 1px solid #2b3139;">
{% if trade_type == 'SPOT' %}
<div class="btn-group w-100 mb-2" role="group">
<input type="radio" class="btn-check" name="trade-side" id="buy-side" checked onchange="toggleSide('BUY')">
<label class="btn btn-outline-success btn-sm py-1" for="buy-side">买入</label>
<input type="radio" class="btn-check" name="trade-side" id="sell-side" onchange="toggleSide('SELL')">
<label class="btn btn-outline-danger btn-sm py-1" for="sell-side">卖出</label>
</div>
<select class="form-select form-select-sm bg-dark text-white border-secondary mb-2" id="spot-mode-select" onchange="toggleModeMobile()">
<option value="limit">限价委托</option>
<option value="market">市价委托</option>
</select>
{% else %}
<div class="d-flex gap-2 mb-2">
<div class="dropdown w-50">
<button class="btn btn-sm btn-dark border-secondary w-100 text-warning fw-bold" id="lev-btn" data-bs-toggle="dropdown">20x</button>
<ul class="dropdown-menu dropdown-menu-dark">
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(10)">10x</a></li>
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(20)">20x</a></li>
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(50)">50x</a></li>
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(100)">100x</a></li>
</ul>
</div>
<select class="form-select form-select-sm bg-dark text-white border-secondary w-50" id="c-mode-select" onchange="toggleModeMobile()">
<option value="limit">限价</option>
<option value="market">市价</option>
</select>
</div>
{% endif %}
<div class="mb-2">
<div class="input-group input-group-sm">
<span class="input-group-text bg-dark border-secondary text-secondary" style="width: 40px; font-size: 10px;"></span>
<input type="text" id="m-price" class="form-control bg-transparent text-white border-secondary" placeholder="价格">
</div>
</div>
<div class="mb-1">
<div class="input-group input-group-sm">
<span class="input-group-text bg-dark border-secondary text-secondary" style="width: 40px; font-size: 10px;" id="m-label"></span>
<input type="number" id="m-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSliderMobile()">
<span class="input-group-text bg-dark border-secondary text-secondary x-small" id="m-unit">{{ base_symbol }}</span>
</div>
</div>
<div class="mb-3 px-1">
<input type="range" class="form-range custom-range" id="m-slider" min="0" max="100" step="1" value="0" oninput="applySliderMobile()">
<div class="d-flex justify-content-between x-small text-secondary" style="font-size: 9px;">
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
</div>
</div>
<div class="x-small mb-2 d-flex justify-content-between text-secondary" style="font-size: 10px;">
<span>可用:</span>
<span id="m-available" class="text-white">--</span>
</div>
{% if trade_type == 'CONTRACT' %}
<div class="row g-1">
<div class="col-6"><button class="btn btn-success btn-sm w-100 fw-bold" onclick="submitOrderMobile('BUY')">开多</button></div>
<div class="col-6"><button class="btn btn-danger btn-sm w-100 fw-bold" onclick="submitOrderMobile('SELL')">开空</button></div>
</div>
{% else %}
<button class="btn btn-success w-100 fw-bold btn-sm" id="m-submit-btn" onclick="submitOrderMobile(side)">买入</button>
{% endif %}
</div>
</div>
</div>
<!-- Chart Tab -->
<div class="glass-card p-1 mb-2" style="background: #161a1e; border: 1px solid #2b3139;">
<div id="tradingview_widget" style="height: 250px;"></div>
</div>
<!-- Positions List -->
<div class="glass-card p-2" style="background: #161a1e; border: 1px solid #2b3139;">
<div class="fw-bold mb-2 small border-bottom border-secondary pb-1 d-flex justify-content-between">
<span>当前持仓/挂单</span>
<span class="text-warning x-small" style="font-weight: normal; cursor: pointer;" onclick="location.reload()"><i class="bi bi-arrow-clockwise"></i> 刷新</span>
</div>
<div id="m-positions-list">
{% if trade_type == 'CONTRACT' %}
{% for p in account.positions.all %}
{% if p.is_active %}
<div class="mb-2 p-2 bg-dark rounded border-start border-3 {% if p.side == 'LONG' %}border-success{% else %}border-danger{% endif %}" style="border: 1px solid #2b3139;">
<div class="d-flex justify-content-between x-small mb-1">
<span class="fw-bold text-white">{{ p.symbol }} <span class="badge bg-secondary">{{ p.leverage }}x</span> <span class="{% if p.side == 'LONG' %}text-success{% else %}text-danger{% endif %}">{% if p.side == 'LONG' %}多{% else %}空{% endif %}</span></span>
<span class="upl-val fw-bold" data-entry="{{ p.entry_price }}" data-side="{{ p.side }}" data-lots="{{ p.lots }}">--</span>
</div>
<div class="d-flex justify-content-between x-small text-secondary" style="font-size: 10px;">
<span>手数: {{ p.lots }}</span>
<span>开仓价: {{ p.entry_price|floatformat:2 }}</span>
</div>
<div class="d-flex justify-content-between x-small text-secondary mb-1" style="font-size: 10px;">
<span>保证金: {{ p.margin|floatformat:2 }}</span>
<span class="current-p-val" data-symbol="{{ p.symbol }}">当前: --</span>
</div>
<button class="btn btn-outline-danger btn-xs py-0 w-100 mt-1" style="font-size: 11px;" onclick="closePos({{ p.id }})">市价平仓</button>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% for o in account.orders.all %}
{% if o.status == 'PENDING' %}
<div class="mb-2 p-2 bg-dark rounded border-start border-3 border-warning" style="border: 1px solid #2b3139;">
<div class="d-flex justify-content-between x-small">
<span class="text-white">{{ o.symbol }} ({{ o.get_trade_type_display }}) <span class="{% if o.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ o.get_side_display }}</span></span>
<span class="text-warning">委托中</span>
</div>
<div class="d-flex justify-content-between x-small text-secondary mt-1" style="font-size: 10px;">
<span>价格: {{ o.price|default:"市价" }}</span>
<span>数量: {{ o.amount }}</span>
</div>
<button class="btn btn-outline-secondary btn-xs py-0 w-100 mt-1" style="font-size: 11px;">撤单</button>
</div>
{% endif %}
{% endfor %}
</div>
</div>
<style>
.btn-xs { padding: 1px 5px; font-size: 10px; }
.order-row { display: flex; justify-content: space-between; margin-bottom: 2px; height: 16px; overflow: hidden; position: relative; }
.order-row span { position: relative; z-index: 1; }
.ask-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(246, 70, 93, 0.1); }
.bid-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(14, 203, 129, 0.1); }
.custom-range::-webkit-slider-thumb { background: #f0b90b; }
</style>
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script>
const symbol = '{{ symbol }}';
const tradeType = '{{ trade_type }}';
const baseAsset = '{{ base_symbol }}';
const balance = parseFloat("{{ account.balance|default:0 }}");
const assetBalance = parseFloat("{{ base_asset_balance|default:0 }}");
let currentPrice = 0;
let leverage = 20;
let side = 'BUY';
function initTV() {
new TradingView.widget({
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
"interval": "15", "theme": "dark", "style": "1", "locale": "zh_CN",
"container_id": "tradingview_widget", "hide_side_toolbar": true, "save_image": false
});
}
async function getMarkets() {
try {
const r = await fetch('/api/market_data/');
const data = await r.json();
const list = document.getElementById('mobile-coin-list');
list.innerHTML = '';
data.forEach(c => {
const b = c.symbol.replace('USDT', '');
const iconUrl = `https://static.okx.com/cdn/oksupport/asset/currency/icon/${b.toLowerCase()}.png`;
list.innerHTML += `<li><a class="dropdown-item d-flex justify-content-between" href="?symbol=${c.symbol}"><span><img src="${iconUrl}" style="width:16px;margin-right:5px;" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${b}</span><span class="${c.change>=0?'text-success':'text-danger'}">${c.price}</span></a></li>`;
});
} catch(e) {}
}
function toggleSide(s) {
side = s;
const btn = document.getElementById('m-submit-btn');
if (btn) {
if (s === 'BUY') {
btn.className = 'btn btn-success w-100 fw-bold btn-sm';
btn.textContent = '立即买入';
} else {
btn.className = 'btn btn-danger w-100 fw-bold btn-sm';
btn.textContent = '立即卖出';
}
}
const avail = document.getElementById('m-available');
if (tradeType === 'SPOT') {
if (s === 'BUY') avail.textContent = balance.toFixed(2) + ' USDT';
else avail.textContent = assetBalance.toFixed(4) + ' ' + baseAsset;
} else {
avail.textContent = balance.toFixed(2) + ' USDT';
}
toggleModeMobile();
}
function toggleModeMobile() {
const mode = tradeType === 'SPOT'
? document.getElementById('spot-mode-select').value
: document.getElementById('c-mode-select').value;
const pInput = document.getElementById('m-price');
const unit = document.getElementById('m-unit');
const label = document.getElementById('m-label');
if (mode === 'market') {
pInput.value = currentPrice || '市价';
pInput.disabled = true;
pInput.style.color = '#f0b90b';
if (tradeType === 'SPOT' && side === 'BUY') {
label.textContent = '额';
unit.textContent = 'USDT';
} else {
label.textContent = '数';
unit.textContent = tradeType === 'SPOT' ? baseAsset : '张';
}
} else {
pInput.value = '';
pInput.disabled = false;
pInput.style.color = 'white';
label.textContent = '数';
unit.textContent = tradeType === 'SPOT' ? baseAsset : '张';
}
applySliderMobile();
}
function applySliderMobile() {
const val = document.getElementById('m-slider').value;
const mode = tradeType === 'SPOT'
? document.getElementById('spot-mode-select').value
: document.getElementById('c-mode-select').value;
const amountInput = document.getElementById('m-amount');
if (tradeType === 'SPOT') {
if (side === 'BUY') {
if (mode === 'market') {
amountInput.value = (balance * (val / 100)).toFixed(2);
} else {
const price = parseFloat(document.getElementById('m-price').value) || currentPrice;
if (price > 0) amountInput.value = (balance * (val / 100) / price).toFixed(4);
else amountInput.value = 0;
}
} else {
amountInput.value = (assetBalance * (val / 100)).toFixed(4);
}
} else {
const maxLots = (balance * leverage) / 100;
amountInput.value = Math.floor(maxLots * (val / 100));
}
}
function updateSliderMobile() {
const val = parseFloat(document.getElementById('m-amount').value) || 0;
const slider = document.getElementById('m-slider');
if (tradeType === 'SPOT') {
if (side === 'BUY') {
const mode = document.getElementById('spot-mode-select').value;
if (mode === 'market') {
if (balance > 0) slider.value = (val / balance) * 100;
} else {
const price = parseFloat(document.getElementById('m-price').value) || currentPrice;
if (balance > 0 && price > 0) slider.value = (val * price / balance) * 100;
}
} else {
if (assetBalance > 0) slider.value = (val / assetBalance) * 100;
}
} else {
const maxLots = (balance * leverage) / 100;
if (maxLots > 0) slider.value = (val / maxLots) * 100;
}
}
function setLev(l) {
leverage = l;
document.getElementById('lev-btn').textContent = l + 'x';
applySliderMobile();
}
async function tick() {
try {
const r = await fetch('/api/market_data/');
const data = await r.json();
const d = data.find(c => c.symbol === symbol);
if (!d) return;
currentPrice = parseFloat(d.price);
document.getElementById('m-current-price').textContent = currentPrice.toLocaleString();
const pInput = document.getElementById('m-price');
if (pInput && pInput.disabled) pInput.value = currentPrice;
const askD = document.getElementById('m-asks'); const bidD = document.getElementById('m-bids');
askD.innerHTML = ''; bidD.innerHTML = '';
for (let i = 0; i < 5; i++) {
const ap = (currentPrice+(5-i)*0.1).toFixed(2);
const bp = (currentPrice-(i+1)*0.1).toFixed(2);
askD.innerHTML = `<div class="order-row"><span class="text-danger">${ap}</span><span class="text-secondary">${(Math.random()*0.5).toFixed(3)}</span><div class="ask-bg" style="width:${Math.random()*60}%"></div></div>` + askD.innerHTML;
bidD.innerHTML += `<div class="order-row"><span class="text-success">${bp}</span><span class="text-secondary">${(Math.random()*0.5).toFixed(3)}</span><div class="bid-bg" style="width:${Math.random()*60}%"></div></div>`;
}
document.querySelectorAll('.current-p-val').forEach(el => { el.textContent = '当前: ' + currentPrice.toLocaleString(); });
document.querySelectorAll('.upl-val').forEach(el => {
const entry = parseFloat(el.getAttribute('data-entry'));
const sideP = el.getAttribute('data-side');
const lots = parseFloat(el.getAttribute('data-lots'));
let upl = sideP === 'LONG' ? (currentPrice - entry) / entry * (lots * 100) : (entry - currentPrice) / entry * (lots * 100);
el.textContent = (upl >= 0 ? '+' : '') + upl.toFixed(2) + ' USDT';
el.className = 'upl-val fw-bold ' + (upl >= 0 ? 'text-success' : 'text-danger');
});
} catch(e) {}
}
async function submitOrderMobile(sideParam) {
const mode = tradeType === 'SPOT' ? document.getElementById('spot-mode-select').value : document.getElementById('c-mode-select').value;
const price = document.getElementById('m-price').value;
const amount = document.getElementById('m-amount').value;
if (mode === 'limit' && (!price || isNaN(parseFloat(price)))) {
alert('请输入有效委托价格'); return;
}
if (!amount || amount <= 0) {
alert('请输入数量'); return;
}
try {
const r = await fetch('{% url "core:submit_order" %}', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' },
body: JSON.stringify({
symbol: symbol, side: sideParam, trade_type: tradeType, order_type: mode.toUpperCase(),
price: mode === 'market' ? null : price, amount: amount, leverage: leverage
})
});
const res = await r.json();
if (res.status === 'success') { alert('下单成功!'); location.reload(); }
else { alert('失败: ' + res.message); }
} catch(e) { alert('提交出错'); }
}
async function closePos(id) {
if (confirm('确定平仓?')) {
const r = await fetch(`/api/close_position/${id}/`, { method: 'POST', headers: { 'X-CSRFToken': '{{ csrf_token }}' } });
const res = await r.json();
if (res.status === 'success') location.reload();
}
}
initTV(); getMarkets(); tick();
setInterval(tick, 2000);
toggleSide('BUY');
</script>
{% endblock %}

View File

@ -0,0 +1,37 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="p-3">
<div class="mb-4">
<a href="{% url 'core:profile' %}" class="text-decoration-none text-secondary small"><i class="bi bi-chevron-left"></i> 返回</a>
<h4 class="fw-bold mt-2">实名认证</h4>
</div>
<div class="glass-card p-4 text-center">
{% if account.kyc_status == 'UNVERIFIED' %}
<i class="bi bi-shield-lock fs-1 text-secondary mb-3"></i>
<h5 class="fw-bold">未认证</h5>
<p class="text-secondary small mb-4">完成实名认证以提高账户额度和安全性</p>
<form method="POST">
{% csrf_token %}
<div class="mb-3 text-start">
<label class="form-label small text-secondary">真实姓名</label>
<input type="text" class="form-control" required placeholder="请输入您的真实姓名">
</div>
<div class="mb-4 text-start">
<label class="form-label small text-secondary">身份证号 / 护照号</label>
<input type="text" class="form-control" required placeholder="请输入证件号码">
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold">提交审核</button>
</form>
{% elif account.kyc_status == 'PENDING' %}
<i class="bi bi-clock-history fs-1 text-warning mb-3"></i>
<h5 class="fw-bold">审核中</h5>
<p class="text-secondary small">您的认证申请正在处理中,请耐心等待。</p>
{% elif account.kyc_status == 'VERIFIED' %}
<i class="bi bi-patch-check-fill fs-1 text-success mb-3"></i>
<h5 class="fw-bold">已认证</h5>
<p class="text-secondary small">您的账户已通过实名认证。</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends 'core/mobile/base_mobile.html' %}
{% block content %}
<div class="p-3">
<div class="mb-4">
<a href="{% url 'core:profile' %}" class="text-decoration-none text-secondary small"><i class="bi bi-chevron-left"></i> 返回</a>
<h4 class="fw-bold mt-2">提现 USDT</h4>
</div>
<div class="glass-card p-3 mb-3">
<div class="d-flex justify-content-between x-small mb-1">
<span class="text-secondary">可用余额</span>
<span class="fw-bold">{{ account.balance }} USDT</span>
</div>
</div>
<form method="POST" class="glass-card p-3">
{% csrf_token %}
<div class="mb-3">
<label class="form-label small text-secondary">提币网络</label>
<select class="form-select form-select-sm bg-dark text-white border-secondary">
<option>TRC20</option>
<option>ERC20</option>
</select>
</div>
<div class="mb-3">
<label class="form-label small text-secondary">提币地址</label>
<input type="text" name="address" class="form-control" placeholder="请输入收币地址" required>
</div>
<div class="mb-3">
<label class="form-label small text-secondary">提币数量</label>
<div class="input-group">
<input type="number" name="amount" class="form-control" placeholder="最小提币 10" required>
<button type="button" class="btn btn-outline-warning x-small" onclick="document.getElementsByName('amount')[0].value={{ account.balance }}">全部</button>
</div>
<div class="x-small text-secondary mt-1">手续费: 1 USDT</div>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold">立即提现</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,222 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container py-5" style="min-height: 90vh;">
<!-- Profile Header -->
<div class="glass-card p-4 mb-4" style="border-radius: 12px; border: 1px solid #2b3139;">
<div class="row align-items-center">
<div class="col-auto">
<div class="avatar-circle bg-warning text-dark d-flex align-items-center justify-content-center fw-bold fs-3" style="width: 80px; height: 80px; border-radius: 50%;">
{{ user.username|first|upper }}
</div>
</div>
<div class="col">
<h2 class="fw-bold mb-1 text-white">{{ user.username }}</h2>
<div class="d-flex gap-3 align-items-center flex-wrap">
<span class="text-secondary small">UID: 59302{{ user.id }}</span>
<span class="badge {% if account.kyc_status == 'completed' %}bg-success{% elif account.kyc_status == 'pending' %}bg-warning text-dark{% else %}bg-secondary{% endif %}">
{% if account.kyc_status == 'completed' %}已认证{% elif account.kyc_status == 'pending' %}审核中{% else %}未认证{% endif %}
</span>
<span class="text-secondary small"><i class="bi bi-clock me-1"></i>注册时间: {{ user.date_joined|date:"Y-m-d" }}</span>
</div>
</div>
<div class="col-lg-auto mt-3 mt-lg-0">
<div class="d-flex gap-2">
<a href="{% url 'core:deposit' %}" class="btn btn-warning fw-bold px-4">充币</a>
<a href="{% url 'core:withdraw' %}" class="btn btn-outline-light px-4">提币</a>
</div>
</div>
</div>
</div>
<div class="row g-4">
<!-- Left Column: Asset Overview -->
<div class="col-lg-4">
<div class="glass-card p-4 h-100" style="border-radius: 12px; border: 1px solid #2b3139;">
<h5 class="fw-bold mb-4">资产总览</h5>
<div class="mb-4">
<p class="text-secondary small mb-1">总资产折合 (USDT)</p>
<h2 class="fw-bold text-white mb-0">≈ {{ account.balance|default:"0.00" }} <span class="fs-5 text-secondary fw-normal">USDT</span></h2>
</div>
<hr class="border-secondary opacity-25">
<div class="asset-item d-flex justify-content-between py-3">
<div>
<div class="fw-bold text-white">现货账户</div>
<div class="text-secondary x-small">Spot Account</div>
</div>
<div class="text-end">
<div class="fw-bold text-white">{{ account.balance|default:"0.00" }} USDT</div>
<div class="text-secondary x-small">≈ $0.00</div>
</div>
</div>
<div class="asset-item d-flex justify-content-between py-3 border-top border-secondary border-opacity-10">
<div>
<div class="fw-bold text-white">合约账户</div>
<div class="text-secondary x-small">Futures Account</div>
</div>
<div class="text-end">
<div class="fw-bold text-white">0.00 USDT</div>
<div class="text-secondary x-small">未开通</div>
</div>
</div>
<div class="asset-item d-flex justify-content-between py-3 border-top border-secondary border-opacity-10">
<div>
<div class="fw-bold text-white">理财账户</div>
<div class="text-secondary x-small">Earn Account</div>
</div>
<div class="text-end">
<div class="fw-bold text-white">0.00 USDT</div>
<div class="text-secondary x-small">年化 3.5%起</div>
</div>
</div>
<a href="#" class="btn btn-outline-secondary w-100 mt-4 btn-sm">查看明细 <i class="bi bi-arrow-right ms-1"></i></a>
</div>
</div>
<!-- Right Column: Security & Activity -->
<div class="col-lg-8">
<!-- Security Center -->
<div class="glass-card p-4 mb-4" style="border-radius: 12px; border: 1px solid #2b3139;">
<h5 class="fw-bold mb-4">安全设置</h5>
<div class="row g-4">
<div class="col-md-6">
<div class="d-flex align-items-start gap-3">
<i class="bi bi-envelope-check fs-4 text-success"></i>
<div>
<div class="fw-bold text-white">邮箱验证</div>
<p class="text-secondary x-small mb-0">已绑定: {{ user.email|default:"未设置" }}</p>
</div>
<button class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">修改</button>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-3">
<i class="bi bi-shield-lock fs-4 text-warning"></i>
<div>
<div class="fw-bold text-white">谷歌验证器</div>
<p class="text-secondary x-small mb-0">用于提币和安全确认</p>
</div>
<button class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">去绑定</button>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-3">
<i class="bi bi-phone fs-4 text-secondary"></i>
<div>
<div class="fw-bold text-white">手机验证</div>
<p class="text-secondary x-small mb-0">未绑定手机号</p>
</div>
<button class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">绑定</button>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-3">
<i class="bi bi-person-badge fs-4 text-warning"></i>
<div>
<div class="fw-bold text-white">身份认证 (KYC)</div>
<p class="text-secondary x-small mb-0">提升提现额度至 100 BTC</p>
</div>
<a href="{% url 'core:verify' %}" class="btn btn-link btn-sm text-warning ms-auto text-decoration-none">立即认证</a>
</div>
</div>
</div>
</div>
<!-- Recent Orders/History -->
<div class="glass-card p-4" style="border-radius: 12px; border: 1px solid #2b3139;">
<ul class="nav nav-tabs border-secondary mb-4" role="tablist">
<li class="nav-item">
<button class="nav-link active bg-transparent border-0 text-white fw-bold px-4" data-bs-toggle="tab" data-bs-target="#orders">当前委托</button>
</li>
<li class="nav-item">
<button class="nav-link bg-transparent border-0 text-secondary fw-bold px-4" data-bs-toggle="tab" data-bs-target="#history">交易历史</button>
</li>
<li class="nav-item">
<button class="nav-link bg-transparent border-0 text-secondary fw-bold px-4" data-bs-toggle="tab" data-bs-target="#funds">资金流水</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="orders">
<div class="text-center py-5">
<i class="bi bi-journal-x display-4 text-secondary opacity-25 mb-3"></i>
<p class="text-secondary">暂无当前委托订单</p>
</div>
</div>
<div class="tab-pane fade" id="history">
<div class="table-responsive">
<table class="table table-dark table-hover x-small">
<thead class="text-secondary">
<tr>
<th>时间</th>
<th>币种</th>
<th>类型</th>
<th>价格</th>
<th>数量</th>
<th>状态</th>
</tr>
</thead>
<tbody>
{% for order in recent_orders %}
<tr>
<td>{{ order.created_at|date:"m-d H:i" }}</td>
<td>{{ order.symbol }}</td>
<td class="{% if order.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ order.side }}</td>
<td>{{ order.entry_price }}</td>
<td>{{ order.amount }}</td>
<td class="text-info">{{ order.status }}</td>
</tr>
{% empty %}
<tr><td colspan="6" class="text-center py-4 text-secondary">暂无记录</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="tab-pane fade" id="funds">
<div class="table-responsive">
<table class="table table-dark table-hover x-small">
<thead class="text-secondary">
<tr>
<th>时间</th>
<th>类型</th>
<th>币种</th>
<th>金额</th>
<th>状态</th>
</tr>
</thead>
<tbody>
{% for tx in recent_transactions %}
<tr>
<td>{{ tx.timestamp|date:"m-d H:i" }}</td>
<td>{{ tx.get_transaction_type_display }}</td>
<td>USDT</td>
<td class="{% if tx.transaction_type == 'deposit' %}text-success{% else %}text-danger{% endif %}">{{ tx.amount }}</td>
<td>{{ tx.get_status_display }}</td>
</tr>
{% empty %}
<tr><td colspan="5" class="text-center py-4 text-secondary">暂无记录</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.x-small { font-size: 11px; }
.asset-item { transition: background 0.2s; cursor: pointer; border-radius: 8px; padding-left: 10px; padding-right: 10px; margin-left: -10px; margin-right: -10px; }
.asset-item:hover { background: rgba(255, 255, 255, 0.05); }
.nav-tabs .nav-link.active { border-bottom: 3px solid var(--accent-color) !important; color: var(--accent-color) !important; }
</style>
{% endblock %}

View File

@ -0,0 +1,96 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top left, #1e2329 0%, #0b0e11 100%);">
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 500px; border-radius: 16px;">
<div class="text-center mb-5">
<h2 class="fw-bold text-white">注册 {{ site_settings.site_name }} 账户</h2>
<p class="text-secondary">开启您的加密资产财富之旅</p>
</div>
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert alert-danger alert-dismissible fade show bg-transparent text-danger border-danger" role="alert">
{{ message }}
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
</div>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">用户名</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
<i class="bi bi-person"></i>
</span>
<input type="text" name="username" class="form-control bg-transparent text-white border-secondary" placeholder="请输入用户名" required>
</div>
</div>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">密码</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
<i class="bi bi-lock"></i>
</span>
<input type="password" name="password" class="form-control bg-transparent text-white border-secondary" placeholder="请输入密码" required>
</div>
</div>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">确认密码</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
<i class="bi bi-lock-fill"></i>
</span>
<input type="password" name="password_confirm" class="form-control bg-transparent text-white border-secondary" placeholder="请再次输入密码" required>
</div>
</div>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">安全验证 ({{ captcha_text }})</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
<i class="bi bi-shield-check"></i>
</span>
<input type="text" name="captcha_input" class="form-control bg-transparent text-white border-secondary" placeholder="请输入计算结果" required>
</div>
</div>
<div class="mb-4">
<div class="form-check">
<input class="form-check-input bg-transparent border-secondary" type="checkbox" id="terms" required checked>
<label class="form-check-label text-secondary small" for="terms">
我已阅读并同意 <a href="{% url 'core:placeholder' '服务条款' %}" class="text-warning">服务条款</a><a href="{% url 'core:placeholder' '隐私政策' %}" class="text-warning">隐私政策</a>
</label>
</div>
</div>
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">创建账户</button>
</form>
<div class="text-center">
<p class="text-secondary small">已有账户? <a href="{% url 'core:login' %}" class="text-warning text-decoration-none fw-bold">立即登录</a></p>
</div>
</div>
</div>
<style>
.auth-card {
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
}
.input-group-text { border-right: none; }
input.form-control { border-left: none; padding: 12px; }
input.form-control:focus {
background: rgba(255, 255, 255, 0.05) !important;
box-shadow: none;
border-color: var(--accent-color);
}
</style>
{% endblock %}

View File

@ -0,0 +1,553 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container-fluid px-2 py-2" style="background-color: #0b0e11; min-height: 90vh;">
<div class="row g-2">
<!-- Left: Coin List Sidebar (Desktop Only) -->
<div class="col-lg-2 d-none d-lg-block">
<div class="glass-card h-100 p-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
<div class="p-2 mb-2">
<div class="input-group input-group-sm">
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
<input type="text" id="coin-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterCoins()">
</div>
</div>
<div class="coin-list-container" style="max-height: calc(100vh - 160px); overflow-y: auto;">
<table class="table table-dark table-hover table-sm mb-0 align-middle" style="font-size: 12px;">
<thead>
<tr class="text-secondary">
<th class="border-0">币种</th>
<th class="border-0 text-end">价格</th>
<th class="border-0 text-end">24h</th>
</tr>
</thead>
<tbody id="left-coin-list">
</tbody>
</table>
</div>
</div>
</div>
<!-- Middle: Main Trading Area -->
<div class="col-lg-7">
<!-- Market Info Bar -->
<div class="glass-card mb-2 p-2 d-flex align-items-center justify-content-between" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
<div class="d-flex align-items-center">
<div class="dropdown me-4">
<button class="btn btn-transparent text-warning fw-bold fs-5 p-0" type="button" data-bs-toggle="dropdown">
<span id="current-symbol-display">{{ symbol }}</span> <i class="bi bi-caret-down-fill small"></i>
</button>
<ul class="dropdown-menu dropdown-menu-dark shadow-lg" id="mobile-coin-list" style="max-height: 400px; overflow-y: auto; width: 250px;">
</ul>
</div>
<div class="me-4">
<div class="fw-bold fs-5 text-success" id="header-price">--</div>
<div class="text-secondary x-small">≈ $<span id="header-price-usd">--</span></div>
</div>
<div class="me-4 text-center">
<div class="text-secondary x-small">24h 涨跌</div>
<div class="fw-bold" id="header-change">--</div>
</div>
<div class="text-center d-none d-md-block">
<div class="text-secondary x-small">24h 成交量(USDT)</div>
<div class="fw-bold small" id="header-volume">--</div>
</div>
</div>
<div class="d-flex gap-1 bg-dark p-1" style="border-radius: 4px;">
<a href="{% url 'core:trade' 'spot' %}?symbol={{ symbol }}" class="btn btn-sm {% if trade_type == 'SPOT' %}btn-warning text-dark{% else %}btn-transparent text-secondary{% endif %} fw-bold px-3">现货</a>
<a href="{% url 'core:trade' 'contract' %}?symbol={{ symbol }}" class="btn btn-sm {% if trade_type == 'CONTRACT' %}btn-warning text-dark{% else %}btn-transparent text-secondary{% endif %} fw-bold px-3">合约</a>
</div>
</div>
<!-- TradingView Chart -->
<div class="glass-card mb-2" style="height: 450px; border-radius: 4px; border: 1px solid #2b3139;">
<div id="tradingview_widget" style="height: 100%;"></div>
</div>
<!-- Trading Forms -->
<div class="glass-card p-3 mb-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
{% if trade_type == 'SPOT' %}
<div class="row g-4">
<!-- Buy -->
<div class="col-md-6 border-end border-secondary">
<div class="btn-group w-100 mb-3" role="group">
<input type="radio" class="btn-check" name="buy-mode" id="buy-limit" checked onchange="toggleMode('buy', 'limit')">
<label class="btn btn-outline-secondary btn-sm" for="buy-limit">限价委托</label>
<input type="radio" class="btn-check" name="buy-mode" id="buy-market" onchange="toggleMode('buy', 'market')">
<label class="btn btn-outline-secondary btn-sm" for="buy-market">市价委托</label>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
<input type="text" id="buy-price" class="form-control bg-transparent text-white border-secondary" placeholder="请输入价格">
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;" id="buy-label">数量</span>
<input type="number" id="buy-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSlider('buy')">
<span class="input-group-text bg-dark text-secondary border-secondary" id="buy-unit">{{ base_symbol }}</span>
</div>
<div class="mb-4 px-1">
<input type="range" class="form-range custom-range" id="buy-slider" min="0" max="100" step="1" value="0" oninput="applySlider('buy')">
<div class="d-flex justify-content-between x-small text-secondary mt-1">
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
</div>
</div>
<div class="d-flex justify-content-between x-small mb-3">
<span class="text-secondary">可用余额</span>
<span class="text-white">{{ account.balance|default:"0.00" }} USDT</span>
</div>
<button class="btn btn-success w-100 fw-bold py-2 shadow" onclick="submitOrder('BUY')">买入 <span class="base-asset">{{ base_symbol }}</span></button>
</div>
<!-- Sell -->
<div class="col-md-6">
<div class="btn-group w-100 mb-3" role="group">
<input type="radio" class="btn-check" name="sell-mode" id="sell-limit" checked onchange="toggleMode('sell', 'limit')">
<label class="btn btn-outline-secondary btn-sm" for="sell-limit">限价委托</label>
<input type="radio" class="btn-check" name="sell-mode" id="sell-market" onchange="toggleMode('sell', 'market')">
<label class="btn btn-outline-secondary btn-sm" for="sell-market">市价委托</label>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
<input type="text" id="sell-price" class="form-control bg-transparent text-white border-secondary" placeholder="请输入价格">
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">数量</span>
<input type="number" id="sell-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSlider('sell')">
<span class="input-group-text bg-dark text-secondary border-secondary">{{ base_symbol }}</span>
</div>
<div class="mb-4 px-1">
<input type="range" class="form-range custom-range-danger" id="sell-slider" min="0" max="100" step="1" value="0" oninput="applySlider('sell')">
<div class="d-flex justify-content-between x-small text-secondary mt-1">
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
</div>
</div>
<div class="d-flex justify-content-between x-small mb-3">
<span class="text-secondary">可用 <span class="base-asset">{{ base_symbol }}</span></span>
<span class="text-white" id="sell-available-display">{{ base_asset_balance|default:"0.00" }} {{ base_symbol }}</span>
</div>
<button class="btn btn-danger w-100 fw-bold py-2 shadow" onclick="submitOrder('SELL')">卖出 <span class="base-asset">{{ base_symbol }}</span></button>
</div>
</div>
{% else %}
<!-- Contract Form -->
<div class="row g-4">
<div class="col-12 mb-2 d-flex gap-3 align-items-center">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="c-mode" id="c-limit" checked onchange="toggleMode('contract', 'limit')">
<label class="btn btn-outline-warning btn-sm px-4" for="c-limit">限价委托</label>
<input type="radio" class="btn-check" name="c-mode" id="c-market" onchange="toggleMode('contract', 'market')">
<label class="btn btn-outline-warning btn-sm px-4" for="c-market">市价委托</label>
</div>
<div class="dropdown">
<button class="btn btn-dark btn-sm border-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" id="lev-btn">杠杆: 20x</button>
<ul class="dropdown-menu dropdown-menu-dark">
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(10)">10x</a></li>
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(20)">20x</a></li>
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(50)">50x</a></li>
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(100)">100x</a></li>
</ul>
</div>
</div>
<div class="col-md-6 border-end border-secondary">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
<input type="text" id="c-price" class="form-control bg-transparent text-white border-secondary" placeholder="请输入价格">
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">手数</span>
<input type="number" id="c-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0" oninput="updateSlider('contract')">
<span class="input-group-text bg-dark text-secondary border-secondary"></span>
</div>
<div class="mb-4 px-1">
<input type="range" class="form-range custom-range" id="c-slider" min="0" max="100" step="1" value="0" oninput="applySlider('contract')">
<div class="d-flex justify-content-between x-small text-secondary mt-1">
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
</div>
</div>
<div class="d-flex justify-content-between x-small mb-3 text-secondary">
<span>所需保证金: <span class="text-white" id="c-margin-display">0.00</span> USDT</span>
<span>可用: <span class="text-white">{{ account.balance|default:"0.00" }} USDT</span></span>
</div>
<div class="row g-2">
<div class="col-6"><button class="btn btn-success w-100 fw-bold shadow" onclick="submitOrder('BUY')">买入 (做多)</button></div>
<div class="col-6"><button class="btn btn-danger w-100 fw-bold shadow" onclick="submitOrder('SELL')">卖出 (做空)</button></div>
</div>
</div>
<div class="col-md-6">
<div class="p-3 bg-dark rounded border border-secondary" style="font-size: 12px;">
<div class="d-flex justify-content-between mb-2"><span>合约面值</span><span class="text-white">100 USDT / 张</span></div>
<div class="d-flex justify-content-between mb-2"><span>当前杠杆</span><span class="text-warning" id="lev-val">20x</span></div>
<div class="d-flex justify-content-between mb-2"><span>预计手续费</span><span class="text-white">0.05%</span></div>
<div class="d-flex justify-content-between"><span>维持保证金率</span><span class="text-white">0.4%</span></div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Positions / Orders Tabs -->
<div class="glass-card p-3" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
<ul class="nav nav-tabs border-0 mb-3" id="tradeTabs" role="tablist">
<li class="nav-item">
<button class="nav-link active bg-transparent text-white border-0 fw-bold border-bottom border-warning border-3" id="positions-tab" data-bs-toggle="tab" data-bs-target="#positions" type="button">当前持仓/订单</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="positions">
<div class="table-responsive">
<table class="table table-dark table-sm align-middle mb-0" style="font-size: 12px;">
<thead>
<tr class="text-secondary">
<th>合约/现货</th>
<th>方向</th>
<th>数量/手数</th>
<th>开仓/委托价</th>
<th>当前价</th>
<th>保证金</th>
<th>盈亏</th>
<th class="text-end">操作</th>
</tr>
</thead>
<tbody>
{% if trade_type == 'CONTRACT' %}
{% for p in account.positions.all %}
{% if p.is_active %}
<tr>
<td class="fw-bold">{{ p.symbol }} {{ p.leverage }}x</td>
<td class="{% if p.side == 'LONG' %}text-success{% else %}text-danger{% endif %}">{{ p.get_side_display }}</td>
<td>{{ p.lots }}</td>
<td>{{ p.entry_price }}</td>
<td class="current-p-val" data-symbol="{{ p.symbol }}">--</td>
<td>{{ p.margin|floatformat:2 }}</td>
<td class="upl-val" data-entry="{{ p.entry_price }}" data-side="{{ p.side }}" data-lots="{{ p.lots }}">--</td>
<td class="text-end"><button class="btn btn-outline-danger btn-sm py-0" onclick="closePos({{ p.id }})">平仓</button></td>
</tr>
{% endif %}
{% endfor %}
{% endif %}
{% for o in account.orders.all %}
{% if o.status == 'PENDING' %}
<tr>
<td>{{ o.symbol }} ({{ o.get_trade_type_display }})</td>
<td class="{% if o.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ o.get_side_display }}</td>
<td>{{ o.amount }}</td>
<td>{{ o.price|default:"市价" }}</td>
<td class="current-p-val" data-symbol="{{ o.symbol }}">--</td>
<td>--</td>
<td>等待成交</td>
<td class="text-end"><button class="btn btn-outline-secondary btn-sm py-0">撤单</button></td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Right: Order Book (Desktop Only) -->
<div class="col-lg-3 d-none d-lg-block">
<div class="glass-card h-100 p-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
<div class="d-flex justify-content-between text-secondary x-small mb-2 px-2">
<span>价格(USDT)</span>
<span>数量({{ base_symbol }})</span>
</div>
<div id="asks" class="mb-2"></div>
<div class="py-2 text-center border-top border-bottom border-secondary my-2">
<span id="current-price-book" class="fs-5 fw-bold text-success">--</span>
</div>
<div id="bids"></div>
</div>
</div>
</div>
</div>
<style>
.x-small { font-size: 11px; }
.coin-icon-small { width: 18px; height: 18px; margin-right: 8px; vertical-align: middle; border-radius: 50%; }
.order-book-row { display: flex; justify-content: space-between; font-size: 11px; padding: 2px 4px; position: relative; }
.order-book-row span { position: relative; z-index: 1; }
.ask-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(246, 70, 93, 0.15); transition: width 0.3s; }
.bid-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(14, 203, 129, 0.15); transition: width 0.3s; }
.custom-range::-webkit-slider-thumb { background: #f0b90b; cursor: pointer; }
.custom-range-danger::-webkit-slider-thumb { background: #f6465d; cursor: pointer; }
.coin-list-container::-webkit-scrollbar { width: 4px; }
.coin-list-container::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 2px; }
.nav-tabs .nav-link:hover { color: #fcd535 !important; }
</style>
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script>
const symbol = '{{ symbol }}';
const tradeType = '{{ trade_type }}';
const balance = parseFloat("{{ account.balance|default:0 }}");
const baseAsset = '{{ base_symbol }}';
const assetBalance = parseFloat("{{ base_asset_balance|default:0 }}");
let currentPrice = 0;
let leverage = 20;
let allCoins = [];
function initTV() {
new TradingView.widget({
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
"interval": "15", "theme": "dark", "style": "1", "locale": "zh_CN",
"container_id": "tradingview_widget", "hide_side_toolbar": false
});
}
async function getMarkets() {
try {
const r = await fetch('/api/market_data/');
const data = await r.json();
allCoins = data;
renderCoins();
} catch(e) {}
}
function renderCoins(filter = '') {
const list = document.getElementById('left-coin-list');
const mobileList = document.getElementById('mobile-coin-list');
if (list) list.innerHTML = '';
if (mobileList) mobileList.innerHTML = '';
allCoins.forEach(c => {
if (filter && !c.symbol.toLowerCase().includes(filter.toLowerCase())) return;
const base = c.symbol.replace('USDT', '');
const chg = parseFloat(c.change);
const iconUrl = `https://static.okx.com/cdn/oksupport/asset/currency/icon/${base.toLowerCase()}.png`;
const row = `
<tr style="cursor: pointer;" onclick="location.href='?symbol=${c.symbol}'">
<td><img src="${iconUrl}" class="coin-icon-small" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${base}</td>
<td class="text-end fw-bold">${parseFloat(c.price).toLocaleString()}</td>
<td class="text-end ${chg>=0?'text-success':'text-danger'}">${chg>=0?'+':''}${chg.toFixed(2)}%</td>
</tr>`;
if (list) list.innerHTML += row;
if (mobileList) mobileList.innerHTML += `<li><a class="dropdown-item d-flex justify-content-between" href="?symbol=${c.symbol}"><span><img src="${iconUrl}" class="coin-icon-small" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${base}</span><span class="${chg>=0?'text-success':'text-danger'}">${parseFloat(c.price).toLocaleString()}</span></a></li>`;
});
}
function filterCoins() {
renderCoins(document.getElementById('coin-search').value);
}
function toggleMode(side, mode) {
let pInput;
if (side === 'buy') {
pInput = document.getElementById('buy-price');
const label = document.getElementById('buy-label');
const unit = document.getElementById('buy-unit');
if (mode === 'market') {
pInput.value = currentPrice || '市价';
pInput.disabled = true;
pInput.style.color = '#f0b90b';
label.textContent = '成交额';
unit.textContent = 'USDT';
} else {
pInput.value = '';
pInput.disabled = false;
pInput.style.color = 'white';
label.textContent = '数量';
unit.textContent = baseAsset;
}
} else if (side === 'sell') {
pInput = document.getElementById('sell-price');
if (mode === 'market') {
pInput.value = currentPrice || '市价';
pInput.disabled = true;
pInput.style.color = '#f0b90b';
} else {
pInput.value = '';
pInput.disabled = false;
pInput.style.color = 'white';
}
} else if (side === 'contract') {
pInput = document.getElementById('c-price');
if (mode === 'market') {
pInput.value = currentPrice || '市价';
pInput.disabled = true;
pInput.style.color = '#f0b90b';
} else {
pInput.value = '';
pInput.disabled = false;
pInput.style.color = 'white';
}
}
}
function applySlider(side) {
const val = document.getElementById(side + '-slider').value;
if (side === 'buy') {
const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market';
if (mode === 'market') {
document.getElementById('buy-amount').value = (balance * (val / 100)).toFixed(2);
} else {
const price = parseFloat(document.getElementById('buy-price').value) || currentPrice;
if (price > 0) document.getElementById('buy-amount').value = (balance * (val / 100) / price).toFixed(4);
else document.getElementById('buy-amount').value = 0;
}
} else if (side === 'sell') {
document.getElementById('sell-amount').value = (assetBalance * (val / 100)).toFixed(4);
} else if (side === 'contract') {
const maxLots = (balance * leverage) / 100;
const lots = Math.floor(maxLots * (val / 100));
document.getElementById('c-amount').value = lots;
const margin = (100 * lots) / leverage;
document.getElementById('c-margin-display').textContent = margin.toFixed(2);
}
}
function updateSlider(side) {
if (side === 'buy') {
const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market';
const amount = parseFloat(document.getElementById('buy-amount').value) || 0;
let percent = 0;
if (mode === 'market') {
percent = (amount / balance) * 100;
} else {
const price = parseFloat(document.getElementById('buy-price').value) || currentPrice;
if (price > 0) percent = (amount * price / balance) * 100;
}
document.getElementById('buy-slider').value = Math.min(100, percent || 0);
} else if (side === 'sell') {
const amount = parseFloat(document.getElementById('sell-amount').value) || 0;
if (assetBalance > 0) document.getElementById('sell-slider').value = Math.min(100, (amount / assetBalance) * 100);
} else if (side === 'contract') {
const lots = parseFloat(document.getElementById('c-amount').value) || 0;
const maxLots = (balance * leverage) / 100;
if (maxLots > 0) document.getElementById('c-slider').value = Math.min(100, (lots / maxLots) * 100);
const margin = (100 * lots) / leverage;
document.getElementById('c-margin-display').textContent = margin.toFixed(2);
}
}
function setLev(l) {
leverage = l;
document.getElementById('lev-btn').textContent = '杠杆: ' + l + 'x';
document.getElementById('lev-val').textContent = l + 'x';
applySlider('contract');
}
async function tick() {
try {
const r = await fetch('/api/market_data/');
const data = await r.json();
const d = data.find(c => c.symbol === symbol);
if (!d) return;
currentPrice = parseFloat(d.price);
document.getElementById('header-price').textContent = currentPrice.toLocaleString();
document.getElementById('header-price-usd').textContent = currentPrice.toLocaleString();
const cpBook = document.getElementById('current-price-book');
if (cpBook) cpBook.textContent = currentPrice.toLocaleString();
// Update market price in box ONLY if disabled (Market Order)
['buy-price', 'sell-price', 'c-price'].forEach(id => {
const el = document.getElementById(id);
if (el && el.disabled) el.value = currentPrice;
});
const chg = parseFloat(d.change);
document.getElementById('header-change').textContent = (chg>=0?'+':'') + chg.toFixed(2) + '%';
document.getElementById('header-change').className = 'fw-bold ' + (chg>=0?'text-success':'text-danger');
const askD = document.getElementById('asks'); const bidD = document.getElementById('bids');
if (askD && bidD) {
askD.innerHTML = ''; bidD.innerHTML = '';
for (let i = 0; i < 10; i++) {
askD.innerHTML = `<div class="order-book-row"><span class="text-danger">${(currentPrice+(10-i)*0.1).toFixed(2)}</span><span>${(Math.random()*1.2).toFixed(4)}</span><div class="ask-bg" style="width:${Math.random()*70}%"></div></div>` + askD.innerHTML;
bidD.innerHTML += `<div class="order-book-row"><span class="text-success">${(currentPrice-(i+1)*0.1).toFixed(2)}</span><span>${(Math.random()*1.2).toFixed(4)}</span><div class="bid-bg" style="width:${Math.random()*70}%"></div></div>`;
}
}
document.querySelectorAll('.current-p-val').forEach(el => { el.textContent = currentPrice.toLocaleString(); });
document.querySelectorAll('.upl-val').forEach(el => {
const entry = parseFloat(el.getAttribute('data-entry'));
const side = el.getAttribute('data-side');
const lots = parseFloat(el.getAttribute('data-lots'));
let upl = 0;
if (side === 'LONG') upl = (currentPrice - entry) / entry * (lots * 100);
else upl = (entry - currentPrice) / entry * (lots * 100);
el.textContent = (upl >= 0 ? '+' : '') + upl.toFixed(2) + ' USDT';
el.className = 'upl-val ' + (upl >= 0 ? 'text-success' : 'text-danger');
});
} catch(e) {}
}
async function submitOrder(side) {
const mode = tradeType === 'SPOT'
? (document.getElementById(side.toLowerCase() + '-limit').checked ? 'LIMIT' : 'MARKET')
: (document.getElementById('c-limit').checked ? 'LIMIT' : 'MARKET');
let price = 0;
let amount = 0;
if (tradeType === 'SPOT') {
price = document.getElementById(side.toLowerCase() + '-price').value;
amount = document.getElementById(side.toLowerCase() + '-amount').value;
} else {
price = document.getElementById('c-price').value;
amount = document.getElementById('c-amount').value;
}
if (mode === 'LIMIT' && !price) {
alert('请输入委托价格');
return;
}
if (!amount || amount <= 0) {
alert('请输入有效数量/手数');
return;
}
try {
const r = await fetch('{% url "core:submit_order" %}', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' },
body: JSON.stringify({
symbol: symbol, side: side, trade_type: tradeType, order_type: mode,
price: mode === 'MARKET' ? null : price, amount: amount, leverage: leverage
})
});
const res = await r.json();
if (res.status === 'success') {
alert('下单成功!');
location.reload();
} else {
alert('下单失败: ' + res.message);
}
} catch(e) { alert('提交出错'); }
}
async function closePos(id) {
if (confirm('确定要平仓吗?')) {
const r = await fetch(`/api/close_position/${id}/`, {
method: 'POST',
headers: { 'X-CSRFToken': '{{ csrf_token }}' }
});
const res = await r.json();
if (res.status === 'success') location.reload();
}
}
initTV(); getMarkets(); tick();
setInterval(tick, 2000);
</script>
{% endblock %}

View File

@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6 animate-up">
<div class="glass-card p-4">
<div class="text-center mb-5">
<div class="bg-primary bg-opacity-10 rounded-circle d-inline-block p-4 mb-3">
<i class="bi bi-person-badge text-primary fs-1"></i>
</div>
<h3 class="fw-bold">身份验证 (KYC)</h3>
<p class="text-secondary">为确保您的账户安全并提高提现额度,请完成身份验证。</p>
</div>
{% if account.kyc_status == 'UNVERIFIED' %}
<div class="row g-4 mb-4">
<div class="col-6">
<div class="p-3 border border-secondary border-opacity-25 rounded-3 text-center">
<i class="bi bi-person-vcard fs-2 text-secondary d-block mb-2"></i>
<span class="small fw-bold text-white">L1 基础认证</span>
<p class="text-secondary x-small mt-2 mb-0">提现额度: 20k USDT/日</p>
</div>
</div>
<div class="col-6">
<div class="p-3 border border-warning border-opacity-50 rounded-3 text-center bg-warning bg-opacity-5">
<i class="bi bi-shield-check fs-2 text-warning d-block mb-2"></i>
<span class="small fw-bold text-white">L2 高级认证</span>
<p class="text-secondary x-small mt-2 mb-0">提现额度: 无限制</p>
</div>
</div>
</div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label class="form-label text-secondary">姓名</label>
<input type="text" class="form-control bg-dark text-white border-secondary border-opacity-25" placeholder="请输入您的真实姓名" required>
</div>
<div class="mb-3">
<label class="form-label text-secondary">证件类型</label>
<select class="form-select bg-dark text-white border-secondary border-opacity-25">
<option>身份证</option>
<option>护照</option>
<option>驾驶证</option>
</select>
</div>
<div class="mb-4">
<label class="form-label text-secondary">证件号码</label>
<input type="text" class="form-control bg-dark text-white border-secondary border-opacity-25" placeholder="请输入您的证件号码" required>
</div>
<div class="row g-3 mb-4">
<div class="col-6">
<label class="form-label text-secondary small">证件正面</label>
<div class="border border-dashed border-secondary border-opacity-50 rounded p-4 text-center cursor-pointer" style="border-style: dashed !important;">
<i class="bi bi-plus-lg text-secondary"></i>
</div>
</div>
<div class="col-6">
<label class="form-label text-secondary small">证件反面</label>
<div class="border border-dashed border-secondary border-opacity-50 rounded p-4 text-center cursor-pointer" style="border-style: dashed !important;">
<i class="bi bi-plus-lg text-secondary"></i>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary-custom w-100 py-3">提交认证</button>
</form>
{% elif account.kyc_status == 'PENDING' %}
<div class="text-center py-5">
<div class="spinner-border text-warning mb-4" role="status"></div>
<h5 class="fw-bold">资料审核中</h5>
<p class="text-secondary">我们正在加速审核您的资料,预计需要 1-2 个工作日。</p>
<a href="/profile/" class="btn btn-outline-light mt-3">返回个人中心</a>
</div>
{% elif account.kyc_status == 'VERIFIED' %}
<div class="text-center py-5">
<i class="bi bi-check-circle-fill text-success" style="font-size: 5rem;"></i>
<h4 class="fw-bold mt-4">认证已成功</h4>
<p class="text-secondary">您已完成身份验证,现在可以享受完整的功能服务。</p>
<a href="/profile/" class="btn btn-outline-light mt-3">返回个人中心</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,74 @@
{% extends "base.html" %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-6 animate-up">
<div class="glass-card p-4">
<h3 class="fw-bold mb-4 text-center">提取数字资产</h3>
<div class="d-flex justify-content-between mb-4">
<span class="text-secondary">可用余额</span>
<span class="fw-bold">{{ account.balance|default:"0.00" }} USDT</span>
</div>
<div class="mb-4">
<label class="form-label text-secondary">提取币种</label>
<div class="d-flex align-items-center bg-dark p-3 rounded border border-secondary border-opacity-25">
<i class="bi bi-coin text-warning fs-4 me-3"></i>
<div class="flex-grow-1">
<h6 class="m-0 fw-bold">USDT</h6>
<small class="text-secondary">Tether US</small>
</div>
<i class="bi bi-chevron-down text-secondary"></i>
</div>
</div>
<form method="POST">
{% csrf_token %}
<div class="mb-4">
<label class="form-label text-secondary">提现网络</label>
<select class="form-select bg-dark text-white border-secondary border-opacity-25 py-2">
<option value="TRC20">TRON (TRC20) - 手续费 1.00 USDT</option>
<option value="ERC20" disabled>Ethereum (ERC20) - 手续费 15.00 USDT</option>
</select>
</div>
<div class="mb-4">
<label class="form-label text-secondary">提现地址</label>
<input type="text" name="address" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="请输入您的提现地址" required>
</div>
<div class="mb-4">
<label class="form-label text-secondary">提现金额</label>
<div class="input-group">
<input type="number" name="amount" id="withdraw-amount" step="0.01" class="form-control bg-dark text-white border-secondary border-opacity-25 py-2" placeholder="最小提现数量 10" required>
<button type="button" class="btn btn-outline-secondary border-secondary border-opacity-25 text-warning" onclick="document.getElementById('withdraw-amount').value = '{{ account.balance }}'">全部</button>
</div>
<div class="d-flex justify-content-between mt-2 small">
<span class="text-secondary">手续费</span>
<span class="text-white">1.00 USDT</span>
</div>
</div>
<div class="bg-warning bg-opacity-10 p-3 rounded mb-4">
<div class="d-flex justify-content-between">
<span class="text-secondary small">实际到账金额</span>
<h5 class="fw-bold text-warning m-0" id="final-amount">0.00 USDT</h5>
</div>
</div>
<button type="submit" class="btn btn-primary-custom w-100 py-3">提交提现申请</button>
</form>
</div>
</div>
</div>
</div>
<script>
document.getElementById('withdraw-amount').addEventListener('input', function(e) {
const val = parseFloat(e.target.value) || 0;
const final = Math.max(0, val - 1).toFixed(2);
document.getElementById('final-amount').innerText = final + ' USDT';
});
</script>
{% endblock %}

View File

@ -1,7 +1,31 @@
from django.urls import path
from . import views
from .views import home
app_name = 'core'
urlpatterns = [
path("", home, name="home"),
path('', views.index, name='index'),
path('trade/<str:trade_type>/', views.trade, name='trade'),
path('spot/', views.trade, {'trade_type': 'spot'}, name='spot_trade'),
path('contract/', views.trade, {'trade_type': 'contract'}, name='contract_trade'),
path('markets/', views.market_center, name='market_center'),
path('profile/', views.profile, name='profile'),
path('deposit/', views.deposit, name='deposit'),
path('withdraw/', views.withdraw, name='withdraw'),
path('verify/', views.verify, name='verify'),
path('login/', views.login_view, name='login'),
path('register/', views.register_view, name='register'),
path('api/market_data/', views.market_data, name='market_data'),
path('api/submit_order/', views.submit_order, name='submit_order'),
path('api/close_position/<int:position_id>/', views.close_position, name='close_position'),
# Dynamic Article/Placeholder Route
path('article/<str:title>/', views.placeholder_view, name='placeholder'),
# Footer links
path('help/', views.placeholder_view, {'title': '帮助中心'}, name='help_center'),
path('support/', views.placeholder_view, {'title': '技术支持'}, name='support'),
path('request/', views.placeholder_view, {'title': '提交请求'}, name='submit_request'),
path('announcements/', views.placeholder_view, {'title': '公告中心'}, name='announcements'),
path('assets/', views.profile, name='asset_management'),
]

View File

@ -1,25 +1,410 @@
import os
import platform
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.http import JsonResponse
from django.contrib.auth.models import User
from django.contrib import messages
from .models import Account, Order, Transaction, Cryptocurrency, SiteSettings, Asset, Position
import random
import decimal
import json
from django.views.decorators.csrf import csrf_exempt
from django.db import transaction
from django import get_version as django_version
from django.shortcuts import render
from django.utils import timezone
def is_mobile(request):
user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
mobile_indicators = ['iphone', 'android', 'phone', 'mobile']
return any(indicator in user_agent for indicator in mobile_indicators)
def index(request):
if is_mobile(request):
return render(request, 'core/mobile/index.html')
return render(request, 'core/index.html')
def home(request):
"""Render the landing screen with loader and environment details."""
host_name = request.get_host().lower()
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
now = timezone.now()
def trade(request, trade_type='spot'):
symbol = request.GET.get('symbol', 'BTCUSDT')
base_symbol = symbol.replace('USDT', '')
cryptos = Cryptocurrency.objects.filter(is_active=True)
account = None
assets = {}
base_asset_balance = decimal.Decimal('0')
if request.user.is_authenticated:
account, _ = Account.objects.get_or_create(user=request.user)
for asset in account.assets.all():
assets[asset.currency] = asset
if asset.currency == base_symbol:
base_asset_balance = asset.balance
context = {
"project_name": "New Style",
"agent_brand": agent_brand,
"django_version": django_version(),
"python_version": platform.python_version(),
"current_time": now,
"host_name": host_name,
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
'symbol': symbol,
'base_symbol': base_symbol,
'trade_type': trade_type.upper(),
'cryptos': cryptos,
'account': account,
'assets': assets,
'base_asset_balance': base_asset_balance,
}
return render(request, "core/index.html", context)
template = 'core/mobile/trade.html' if is_mobile(request) else 'core/trade.html'
return render(request, template, context)
def market_center(request):
cryptos = Cryptocurrency.objects.filter(is_active=True)
template = 'core/mobile/market_center.html' if is_mobile(request) else 'core/market_center.html'
return render(request, template, {'cryptos': cryptos})
def placeholder_view(request, title):
settings = SiteSettings.objects.first()
if title == '服务条款':
content = settings.terms_content if settings and settings.terms_content else '暂无服务条款内容。'
elif title == '隐私政策':
content = settings.privacy_content if settings and settings.privacy_content else '暂无隐私政策内容。'
else:
contents = {
'帮助中心': '欢迎来到 BitCrypto 帮助中心。在这里您可以找到关于账户设置、资产充提、交易指南等所有问题的答案。我们为您准备了详尽的视频教程和图文说明,帮助您快速上手。',
'技术支持': 'BitCrypto 技术支持团队 24/7 在线。如果您遇到任何 API 对接、系统报错或连接问题,请随时联系我们的工程师。我们承诺在 15 分钟内给予首次回复。',
'提交请求': '请在下方表单提交您的需求或反馈。无论是工单申请、投诉建议还是商务合作,我们都会认真对待。您的每一份反馈都是我们前进的动力。',
'公告中心': '查看 BitCrypto 最新动态。包括新币上线通知、系统维护公告、市场活动资讯等。订阅我们的邮件列表,第一时间获取核心商业情报。',
}
content = contents.get(title, f'这是{title}的详细内容。BitCrypto为您提供最优质的服务。')
faqs = [
{'q': '如何进行身份认证?', 'a': '登录后在个人中心点击身份认证,上传身份证件并完成人脸识别即可。'},
{'q': '充值多久能到账?', 'a': '区块链网络确认后自动到账,通常 5-30 分钟。'},
{'q': '手续费是多少?', 'a': '现货交易基础手续费为 0.1%,使用平台币抵扣可享 7.5 折优惠。'},
]
return render(request, 'core/article_detail.html', {
'title': title,
'content': content,
'faqs': faqs if title == '帮助中心' else None
})
@login_required
def profile(request):
account, created = Account.objects.get_or_create(user=request.user)
recent_transactions = Transaction.objects.filter(account=account).order_by('-timestamp')[:10]
recent_orders = Order.objects.filter(account=account).order_by('-created_at')[:10]
positions = Position.objects.filter(account=account, is_active=True)
context = {
'account': account,
'recent_transactions': recent_transactions,
'recent_orders': recent_orders,
'positions': positions,
}
template = 'core/mobile/profile.html' if is_mobile(request) else 'core/profile.html'
return render(request, template, context)
@login_required
def deposit(request):
if request.method == 'POST':
amount = request.POST.get('amount')
tx_id = request.POST.get('tx_id')
if amount and tx_id:
account = request.user.account
Transaction.objects.create(
account=account,
transaction_type='deposit',
amount=decimal.Decimal(amount),
status='pending',
tx_hash=tx_id
)
return redirect('core:profile')
template = 'core/mobile/deposit.html' if is_mobile(request) else 'core/deposit.html'
return render(request, template)
@login_required
def withdraw(request):
account = request.user.account
if request.method == 'POST':
amount = request.POST.get('amount')
address = request.POST.get('address')
if amount and address:
amount_dec = decimal.Decimal(amount)
if account.balance >= amount_dec:
account.balance -= amount_dec
account.save()
Transaction.objects.create(
account=account,
transaction_type='withdraw',
amount=amount_dec,
status='completed',
tx_hash=f"wd_{random.randint(1000, 9999)}"
)
return redirect('core:profile')
template = 'core/mobile/withdraw.html' if is_mobile(request) else 'core/withdraw.html'
return render(request, template, {'account': account})
@login_required
def verify(request):
account = request.user.account
if request.method == 'POST':
account.kyc_status = 'pending'
account.save()
return redirect('core:profile')
template = 'core/mobile/verify.html' if is_mobile(request) else 'core/verify.html'
return render(request, template, {'account': account})
def register_view(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
password_confirm = request.POST.get('password_confirm')
captcha_input = request.POST.get('captcha_input')
captcha_expected = request.session.get('captcha_result')
if password != password_confirm:
messages.error(request, "两次输入的密码不一致")
elif str(captcha_input) != str(captcha_expected):
messages.error(request, "验证码错误")
elif User.objects.filter(username=username).exists():
messages.error(request, "用户名已存在")
else:
user = User.objects.create_user(username=username, password=password)
Account.objects.get_or_create(user=user)
login(request, user)
return redirect('core:index')
captcha_text, captcha_result = generate_captcha()
request.session['captcha_result'] = captcha_result
template = 'core/mobile/register.html' if is_mobile(request) else 'core/register.html'
return render(request, template, {
'captcha_text': captcha_text
})
def generate_captcha():
a = random.randint(1, 10)
b = random.randint(1, 10)
op = random.choice(['+', '-', '*'])
if op == '+': res = a + b
elif op == '-': res = a - b
else: res = a * b
return f"{a} {op} {b} = ?", res
def login_view(request):
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect('core:index')
else:
form = AuthenticationForm()
template = 'core/mobile/login.html' if is_mobile(request) else 'core/login.html'
return render(request, template, {'form': form})
@login_required
@csrf_exempt
def submit_order(request):
if request.method != 'POST':
return JsonResponse({'status': 'error', 'message': 'Invalid request'})
try:
data = json.loads(request.body)
symbol = data.get('symbol', 'BTCUSDT')
side = data.get('side') # BUY or SELL
trade_type = data.get('trade_type', 'SPOT')
order_type = data.get('order_type', 'MARKET')
price_val = data.get('price')
amount_val = data.get('amount', 0)
leverage = int(data.get('leverage', 20))
account = request.user.account
base_symbol = symbol.replace('USDT', '')
# Get price (consider manual override)
crypto = Cryptocurrency.objects.filter(symbol=base_symbol).first()
settings = SiteSettings.objects.first()
current_price = crypto.current_price if (crypto and crypto.current_price > 0) else decimal.Decimal('48000')
if settings and settings.is_pinning_active and crypto and crypto.manual_price:
current_price = crypto.manual_price
with transaction.atomic():
if trade_type == 'SPOT':
if order_type == 'MARKET':
# Spot Market Order: Execute Immediately
if side == 'BUY':
# BUY: amount_val is USDT
total_usdt = decimal.Decimal(str(amount_val))
if account.balance < total_usdt:
return JsonResponse({'status': 'error', 'message': '余额不足'})
exec_amount = total_usdt / current_price
account.balance -= total_usdt
account.save()
asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol)
asset.balance += exec_amount
asset.save()
Order.objects.create(
account=account, symbol=symbol, side=side, order_type=order_type,
trade_type=trade_type, amount=exec_amount, total_usdt=total_usdt, status='FILLED'
)
else: # SELL
# SELL: amount_val is coin quantity
exec_amount = decimal.Decimal(str(amount_val))
asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol)
if asset.balance < exec_amount:
return JsonResponse({'status': 'error', 'message': f'{base_symbol} 余额不足'})
total_usdt = exec_amount * current_price
asset.balance -= exec_amount
asset.save()
account.balance += total_usdt
account.save()
Order.objects.create(
account=account, symbol=symbol, side=side, order_type=order_type,
trade_type=trade_type, amount=exec_amount, total_usdt=total_usdt, status='FILLED'
)
else: # LIMIT
# Spot Limit Order: Freeze Assets, Pend
price = decimal.Decimal(str(price_val))
amount = decimal.Decimal(str(amount_val))
if side == 'BUY':
total_usdt = price * amount
if account.balance < total_usdt:
return JsonResponse({'status': 'error', 'message': '余额不足'})
account.balance -= total_usdt
account.frozen_balance += total_usdt
account.save()
Order.objects.create(
account=account, symbol=symbol, side=side, order_type=order_type,
trade_type=trade_type, amount=amount, price=price, total_usdt=total_usdt, status='PENDING'
)
else: # SELL
asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol)
if asset.balance < amount:
return JsonResponse({'status': 'error', 'message': f'{base_symbol} 余额不足'})
asset.balance -= amount
asset.frozen += amount
asset.save()
Order.objects.create(
account=account, symbol=symbol, side=side, order_type=order_type,
trade_type=trade_type, amount=amount, price=price, status='PENDING'
)
else: # CONTRACT
# Contract: Initial Margin = face_value(100) * lots / leverage
lots = decimal.Decimal(str(amount_val))
face_value = decimal.Decimal('100')
margin_required = (face_value * lots) / decimal.Decimal(str(leverage))
if account.balance < margin_required:
return JsonResponse({'status': 'error', 'message': '保证金不足'})
if order_type == 'MARKET':
# Contract Market Order: Immediate Open Position
account.balance -= margin_required
account.save()
Position.objects.create(
account=account, symbol=symbol, side='LONG' if side == 'BUY' else 'SHORT',
leverage=leverage, entry_price=current_price, lots=lots, margin=margin_required
)
Order.objects.create(
account=account, symbol=symbol, side=side, order_type=order_type,
trade_type=trade_type, amount=lots, leverage=leverage, status='FILLED'
)
else: # LIMIT
# Contract Limit Order: Freeze Margin, Pend
price = decimal.Decimal(str(price_val))
account.balance -= margin_required
account.frozen_balance += margin_required
account.save()
Order.objects.create(
account=account, symbol=symbol, side=side, order_type=order_type,
trade_type=trade_type, amount=lots, price=price, leverage=leverage, status='PENDING'
)
return JsonResponse({'status': 'success'})
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)})
@login_required
@csrf_exempt
def close_position(request, position_id):
if request.method != 'POST':
return JsonResponse({'status': 'error', 'message': 'Invalid request'})
position = get_object_or_404(Position, id=position_id, account=request.user.account, is_active=True)
base_symbol = position.symbol.replace('USDT', '')
crypto = Cryptocurrency.objects.filter(symbol=base_symbol).first()
settings = SiteSettings.objects.first()
current_price = crypto.current_price if (crypto and crypto.current_price > 0) else decimal.Decimal('48000')
if settings and settings.is_pinning_active and crypto and crypto.manual_price:
current_price = crypto.manual_price
account = request.user.account
face_value = decimal.Decimal('100')
# Win/Loss Control Logic
# 100 = Must Win, -100 = Must Loss
forced_upl = None
if account.win_loss_control != 0:
# Force a small profit or loss
total_value = position.lots * face_value
if account.win_loss_control > 0: # Force Profit
forced_upl = total_value * decimal.Decimal('0.05') # 5% profit
else: # Force Loss
forced_upl = -total_value * decimal.Decimal('0.05') # 5% loss
# Calculate Unrealized P&L
if position.side == 'LONG':
upl = (current_price - position.entry_price) / position.entry_price * (position.lots * face_value)
else:
upl = (position.entry_price - current_price) / position.entry_price * (position.lots * face_value)
# If control is active, override upl
if forced_upl is not None:
upl = forced_upl
with transaction.atomic():
# Settlement: Margin + P&L - Fee (0.05%)
fee = (position.lots * face_value) * decimal.Decimal('0.0005')
account.balance += (position.margin + upl - fee)
account.save()
position.is_active = False
position.save()
# Log closing order
Order.objects.create(
account=account, symbol=position.symbol, side='SELL' if position.side == 'LONG' else 'BUY',
order_type='MARKET', trade_type='CONTRACT', amount=position.lots, status='FILLED'
)
return JsonResponse({'status': 'success'})
def market_data(request):
settings = SiteSettings.objects.first()
cryptos = Cryptocurrency.objects.filter(is_active=True)
data = []
for c in cryptos:
price = float(c.current_price)
if settings and settings.is_pinning_active and c.manual_price:
price = float(c.manual_price)
symbol_display = c.symbol if 'USDT' in c.symbol else f"{c.symbol}USDT"
data.append({
'symbol': symbol_display,
'price': price,
'change': float(c.change_24h)
})
return JsonResponse(data, safe=False)

View File

@ -1,17 +1,32 @@
<?php
// Generated by setup_mariadb_project.sh — edit as needed.
/**
* 数据库配置文件 - 请根据您的宝塔面板数据库信息进行修改
*/
// 数据库主机
define('DB_HOST', '127.0.0.1');
// 数据库名称
define('DB_NAME', 'app_38239');
// 数据库用户名
define('DB_USER', 'app_38239');
// 数据库密码
define('DB_PASS', 'fe602355-1e20-4dc6-b292-71638a106289');
function db() {
static $pdo;
if (!$pdo) {
try {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
}
return $pdo;
}
?>

161
deposit.php Normal file
View File

@ -0,0 +1,161 @@
<?php
include_once 'config.php';
check_auth();
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
$settings = get_site_settings();
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$amount = (float)($_POST['amount'] ?? 0);
$method = $_POST['pay_method'] ?? 'USDT';
$tx_hash = $_POST['tx_hash'] ?? 'FIAT_DEPOSIT_' . time();
if ($amount < 10) {
$error = '最小充值金额为 10 USDT';
} else {
$stmt = db()->prepare("INSERT INTO transactions (account_id, transaction_type, currency, pay_method, amount, tx_hash, status) VALUES (?, 'deposit', 'USDT', ?, ?, ?, 'pending')");
$stmt->execute([$account['id'], $method, $amount, $tx_hash]);
$success = '充值申请已提交,请等待系统确认';
}
}
include 'header.php';
?>
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="glass-card p-4">
<h3 class="text-white mb-4"><i class="bi bi-wallet2 text-warning me-2"></i> 充值中心</h3>
<?php if ($success): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<ul class="nav nav-pills mb-4 bg-dark p-1 rounded" id="depositTab" role="tablist">
<li class="nav-item flex-fill" role="presentation">
<button class="nav-link active w-100 text-white" id="usdt-tab" data-bs-toggle="pill" data-bs-target="#usdt-pane" type="button">数字货币 (USDT)</button>
</li>
<li class="nav-item flex-fill" role="presentation">
<button class="nav-link w-100 text-white" id="fiat-tab" data-bs-toggle="pill" data-bs-target="#fiat-pane" type="button">法币充值 (Bank/Alipay)</button>
</li>
</ul>
<div class="tab-content" id="depositTabContent">
<!-- USDT Pane -->
<div class="tab-pane fade show active" id="usdt-pane">
<div class="row">
<div class="col-md-5 text-center mb-4">
<div class="bg-white p-3 d-inline-block rounded mb-3">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=<?php echo $settings['deposit_address']; ?>" alt="QR" width="160">
</div>
<div class="text-secondary small">扫描上方二维码获取地址</div>
</div>
<div class="col-md-7">
<div class="mb-4">
<label class="text-secondary small d-block mb-1">选择网络</label>
<div class="btn-group w-100">
<button class="btn btn-outline-warning active">TRC20</button>
<button class="btn btn-outline-secondary disabled">ERC20</button>
<button class="btn btn-outline-secondary disabled">BEP20</button>
</div>
</div>
<div class="mb-4">
<label class="text-secondary small d-block mb-1">充值地址</label>
<div class="input-group">
<input type="text" id="addr" class="form-control bg-dark border-secondary text-white" value="<?php echo $settings['deposit_address']; ?>" readonly>
<button class="btn btn-outline-warning" onclick="navigator.clipboard.writeText('<?php echo $settings['deposit_address']; ?>')">复制</button>
</div>
</div>
</div>
</div>
<form method="POST" class="mt-4 border-top border-secondary pt-4">
<input type="hidden" name="pay_method" value="USDT">
<div class="mb-3">
<label class="form-label text-secondary small">充值金额 (USDT)</label>
<input type="number" name="amount" class="form-control bg-dark border-secondary text-white" placeholder="0.00" required>
</div>
<div class="mb-3">
<label class="form-label text-secondary small">交易哈希 / 凭证号</label>
<input type="text" name="tx_hash" class="form-control bg-dark border-secondary text-white" placeholder="请输入 TxID 或 交易流水号" required>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-3">提交充值申请</button>
</form>
</div>
<!-- Fiat Pane -->
<div class="tab-pane fade" id="fiat-pane">
<div class="alert alert-info bg-dark border-secondary text-secondary small mb-4">
<i class="bi bi-info-circle text-warning me-1"></i> 法币充值按实时汇率折算,当前汇率: 1 USDT 7.25 CNY
</div>
<div class="glass-card p-3 mb-4" style="background: rgba(255,255,255,0.03);">
<h6 class="text-white mb-3">收款账户信息</h6>
<div class="d-flex justify-content-between mb-2 small">
<span class="text-secondary">收款银行</span>
<span class="text-white">工商银行 (ICBC)</span>
</div>
<div class="d-flex justify-content-between mb-2 small">
<span class="text-secondary">收款人</span>
<span class="text-white">BitCrypto Technology Co.</span>
</div>
<div class="d-flex justify-content-between mb-2 small">
<span class="text-secondary">银行账号</span>
<span class="text-white">6222 0000 0000 0000 888</span>
</div>
<div class="d-flex justify-content-between mb-0 small">
<span class="text-secondary">开户支行</span>
<span class="text-white">上海自贸区支行</span>
</div>
</div>
<form method="POST">
<input type="hidden" name="pay_method" value="FIAT">
<div class="mb-3">
<label class="form-label text-secondary small">支付方式</label>
<select name="sub_method" class="form-select bg-dark border-secondary text-white">
<option value="BANK">网银转账</option>
<option value="ALIPAY">支付宝转账</option>
<option value="WECHAT">微信支付</option>
</select>
</div>
<div class="mb-3">
<label class="form-label text-secondary small">充值金额 (CNY)</label>
<div class="input-group">
<input type="number" id="fiat_amount" name="fiat_amount" class="form-control bg-dark border-secondary text-white" placeholder="0.00" oninput="document.getElementById('usdt_equiv').value = (this.value / 7.25).toFixed(2)">
<span class="input-group-text bg-dark border-secondary text-secondary">CNY</span>
</div>
</div>
<div class="mb-3">
<label class="form-label text-secondary small">折合 (USDT)</label>
<input type="text" id="usdt_equiv" name="amount" class="form-control bg-dark border-secondary text-white" readonly>
</div>
<div class="mb-3">
<label class="form-label text-secondary small">汇款人姓名</label>
<input type="text" name="payer_name" class="form-control bg-dark border-secondary text-white" placeholder="请填写汇款账户实名" required>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-3">我已完成转账</button>
</form>
</div>
</div>
<div class="mt-4 p-3 rounded" style="background: rgba(255,255,255,0.02); border: 1px solid rgba(255,255,255,0.05);">
<h6 class="text-secondary small mb-2">充值说明:</h6>
<ul class="text-secondary small ps-3 mb-0">
<li>数字货币充值通常在 10-30 分钟内到账。</li>
<li>法币充值需要人工审核,工作时间 (9:00-22:00) 30 分钟内到账。</li>
<li>请务必在汇款备注中填写您的 UID: <span class="text-warning fw-bold"><?php echo $account['uid']; ?></span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<?php include 'footer.php'; ?>

44
footer.php Normal file
View File

@ -0,0 +1,44 @@
<?php
$settings = get_site_settings();
$project_name = $settings['site_name'] ?? 'BitCrypto';
?>
</main>
<?php if (!empty($settings['customer_service_url'])): ?>
<a href="<?php echo $settings['customer_service_url']; ?>" target="_blank" class="cs-float" title="在线客服">
<i class="bi bi-headset"></i>
</a>
<?php endif; ?>
<footer class="footer py-5 mt-5 border-top border-secondary bg-dark">
<div class="container">
<div class="row g-4">
<div class="col-md-3">
<h5 class="fw-bold mb-4 text-white"><?php echo $project_name; ?></h5>
<p class="text-secondary small">全球领先的数字资产交易平台,致力于为用户提供安全、专业、透明的数字资产一站式服务。</p>
</div>
<div class="col-md-2 offset-md-1">
<h6 class="text-white mb-3">产品服务</h6>
<ul class="list-unstyled">
<li><a href="/trade.php?type=SPOT" class="text-secondary text-decoration-none small">现货交易</a></li>
<li><a href="/trade.php?type=CONTRACT" class="text-secondary text-decoration-none small">永续合约</a></li>
</ul>
</div>
</div>
<hr class="my-5 border-secondary">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-start">
<p class="text-secondary small mb-0">&copy; 2026 <?php echo $project_name; ?> Digital Asset Exchange. All rights reserved.</p>
</div>
<div class="col-md-6 text-center text-md-end mt-3 mt-md-0">
<div class="text-secondary small">
<i class="bi bi-globe me-2"></i> 简体中文 | USDT
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

137
header.php Normal file
View File

@ -0,0 +1,137 @@
<?php include_once 'config.php';
$settings = get_site_settings();
$project_name = $settings['site_name'] ?? 'BitCrypto';
?>
<!DOCTYPE html>
<html lang="zh-hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $project_name; ?> - 全球领先的数字资产交易平台</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="/static/css/custom.css">
<style>
:root {
--bg-dark: #0b0e11;
--bg-card: #181a20;
--text-primary: #eaecef;
--text-secondary: #848e9c;
--accent-color: #fcd535;
--up-color: #0ecb81;
--down-color: #f6465d;
}
body {
background-color: var(--bg-dark);
color: var(--text-primary);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 0;
}
.navbar {
background-color: var(--bg-dark);
border-bottom: 1px solid #2b2f36;
padding: 0.75rem 1.5rem;
}
.nav-link {
color: var(--text-primary) !important;
font-weight: 500;
font-size: 14px;
margin: 0 8px;
transition: color 0.2s;
}
.nav-link:hover { color: var(--accent-color) !important; }
.btn-primary {
background-color: var(--accent-color);
border-color: var(--accent-color);
color: #181a20;
font-weight: 600;
padding: 8px 20px;
border-radius: 4px;
}
.glass-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
}
.dropdown-menu-dark {
background-color: #1e2329;
border: 1px solid #3b4149;
}
.dropdown-item {
font-size: 14px;
padding: 10px 20px;
}
.dropdown-item:hover {
background-color: #2b3139;
color: var(--accent-color);
}
.cs-float {
position: fixed;
right: 30px;
bottom: 40px;
width: 60px;
height: 60px;
background: var(--accent-color);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #181a20;
font-size: 28px;
box-shadow: 0 4px 20px rgba(252, 213, 53, 0.4);
cursor: pointer;
z-index: 9999;
transition: all 0.3s;
text-decoration: none;
}
.cs-float:hover {
transform: scale(1.1) rotate(5deg);
color: #181a20;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg sticky-top">
<div class="container-fluid">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/logo.png" alt="Logo" style="height: 32px; margin-right: 12px;">
<span class="fw-bold fs-4 text-white"><?php echo $project_name; ?></span>
</a>
<button class="navbar-toggler border-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<i class="bi bi-list text-white"></i>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="/">首页</a></li>
<li class="nav-item"><a class="nav-link" href="/trade.php?type=SPOT">现货</a></li>
<li class="nav-item"><a class="nav-link" href="/trade.php?type=CONTRACT">合约</a></li>
<li class="nav-item"><a class="nav-link" href="/market.php">行情</a></li>
</ul>
<div class="d-flex align-items-center">
<?php if (isset($_SESSION['user_id'])): ?>
<div class="dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
<i class="bi bi-person-circle fs-5 me-2 text-warning"></i>
<span class="text-white fw-bold"><?php echo $_SESSION['username']; ?></span>
</a>
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end shadow-lg" style="min-width: 220px;">
<li><a class="dropdown-item" href="/profile.php"><i class="bi bi-person-badge me-2 text-secondary"></i>个人中心</a></li>
<li><a class="dropdown-item" href="/verify.php"><i class="bi bi-shield-check me-2 text-secondary"></i>实名认证 (KYC)</a></li>
<li><hr class="dropdown-divider border-secondary"></li>
<li><a class="dropdown-item" href="/deposit.php"><i class="bi bi-plus-circle me-2 text-success"></i>充值</a></li>
<li><a class="dropdown-item" href="/withdraw.php"><i class="bi bi-dash-circle me-2 text-danger"></i>提现</a></li>
<li><hr class="dropdown-divider border-secondary"></li>
<li><a class="dropdown-item text-danger" href="/logout.php"><i class="bi bi-box-arrow-right me-2"></i>安全退出</a></li>
</ul>
</div>
<?php else: ?>
<a href="/login.php" class="nav-link me-3">登录</a>
<a href="/register.php" class="btn btn-primary">立即注册</a>
<?php endif; ?>
</div>
</div>
</div>
</nav>
<main>

184
index.php Normal file
View File

@ -0,0 +1,184 @@
<?php
include 'header.php';
?>
<!-- Hero Section -->
<section class="py-5" style="background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%); min-height: 70vh; display: flex; align-items: center;">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6">
<div class="badge bg-warning text-dark mb-3 px-3 py-2 fw-bold">#1 全球领先交易平台</div>
<h1 class="display-3 fw-bold text-white mb-4">简单、安全、<br>极速 <span class="text-warning">交易加密货币</span></h1>
<p class="lead text-secondary mb-5" style="max-width: 500px;"> BitCrypto 开启您的交易之旅。支持 350+ 种加密货币,提供 100 倍杠杆合约及超低手续费现货交易。</p>
<div class="d-flex gap-3 flex-wrap">
<?php if(!isset($_SESSION['user_id'])): ?>
<a href="register.php" class="btn btn-warning btn-lg px-5 fw-bold py-3 shadow">立即开启</a>
<?php else: ?>
<a href="trade.php" class="btn btn-warning btn-lg px-5 fw-bold py-3 shadow">进入交易</a>
<?php endif; ?>
<a href="market.php" class="btn btn-outline-light btn-lg px-5 py-3">查看行情</a>
</div>
<div class="mt-5 d-flex gap-4 text-secondary small">
<div><i class="bi bi-shield-check text-success me-1"></i> 资产安全保障</div>
<div><i class="bi bi-lightning text-warning me-1"></i> 毫秒级撮合</div>
<div><i class="bi bi-headset text-info me-1"></i> 7/24 在线支持</div>
</div>
</div>
<div class="col-lg-6 d-none d-lg-block">
<div class="position-relative">
<img src="https://public.bnbstatic.com/image/cms/content/body_0b0e11.png" class="img-fluid floating-img" alt="Hero">
<div class="glass-card p-3 position-absolute top-0 start-0 translate-middle mt-5 ms-5 shadow-lg" style="width: 200px; background: rgba(14, 203, 129, 0.2); border: 1px solid rgba(14, 203, 129, 0.4);">
<div class="smaller text-success fw-bold mb-1">BTC/USDT</div>
<div class="fs-4 fw-bold text-white" id="hero-btc-price">--</div>
<div class="text-success smaller">+2.4% <i class="bi bi-graph-up"></i></div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Stats Bar -->
<div class="container mt-n5 position-relative" style="z-index: 10;">
<div class="glass-card p-4 shadow-lg" style="background: rgba(30, 32, 38, 0.95); border: 1px solid #3b4149;">
<div class="row text-center g-4">
<div class="col-md-3 col-6">
<div class="text-secondary small mb-1">24h 交易量</div>
<div class="fs-4 fw-bold text-white">$76.2B</div>
</div>
<div class="col-md-3 col-6 border-start border-secondary border-md-0">
<div class="text-secondary small mb-1">主流币种</div>
<div class="fs-4 fw-bold text-white">350+</div>
</div>
<div class="col-md-3 col-6 border-start border-secondary">
<div class="text-secondary small mb-1">注册用户</div>
<div class="fs-4 fw-bold text-white">120M+</div>
</div>
<div class="col-md-3 col-6 border-start border-secondary">
<div class="text-secondary small mb-1">最低费率</div>
<div class="fs-4 fw-bold text-white">0.02%</div>
</div>
</div>
</div>
</div>
<!-- Market Table -->
<section id="markets" class="py-5 mt-5">
<div class="container">
<div class="d-flex justify-content-between align-items-end mb-4">
<div>
<h2 class="fw-bold text-white">热门币种</h2>
<p class="text-secondary mb-0">同步全球顶级交易所实时数据</p>
</div>
<a href="market.php" class="text-warning text-decoration-none fw-bold">更多市场 <i class="bi bi-arrow-right"></i></a>
</div>
<div class="glass-card overflow-hidden" style="border: 1px solid #2b2f36;">
<table class="table table-dark table-hover mb-0 align-middle">
<thead>
<tr class="text-secondary" style="background: #1e2329;">
<th class="ps-4 py-3">名称</th>
<th class="py-3">最新价</th>
<th class="py-3">24h 涨跌</th>
<th class="py-3 d-none d-md-table-cell">24h 最高 / 最低</th>
<th class="py-3 text-end pe-4">交易</th>
</tr>
</thead>
<tbody id="market-tbody">
<tr><td colspan="5" class="text-center py-5"><div class="spinner-border text-warning"></div></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Mobile Download Promo -->
<section class="py-5" style="background: #181a20;">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-start mb-4 mb-md-0">
<h2 class="text-white fw-bold mb-3">随时随地 尽情交易</h2>
<p class="text-secondary mb-4">下载 BitCrypto 移动端 App在手机上获取实时行情、管理资产、极速下单。</p>
<div class="d-flex gap-3 justify-content-center justify-content-md-start">
<button class="btn btn-outline-light px-4 py-2"><i class="bi bi-apple me-2"></i>App Store</button>
<button class="btn btn-outline-light px-4 py-2"><i class="bi bi-google-play me-2"></i>Google Play</button>
</div>
</div>
<div class="col-md-6 text-center">
<img src="https://public.bnbstatic.com/image/cms/content/body_mobile_app.png" class="img-fluid" style="max-height: 300px;">
</div>
</div>
</div>
</section>
<style>
.floating-img { animation: floating 3s ease-in-out infinite; }
@keyframes floating {
0% { transform: translateY(0px); }
50% { transform: translateY(-20px); }
100% { transform: translateY(0px); }
}
.mt-n5 { margin-top: -3rem !important; }
@media (max-width: 768px) {
.border-md-0 { border: 0 !important; }
}
</style>
<script>
function formatPrice(p) {
p = parseFloat(p);
if (p < 0.0001) return p.toFixed(8);
if (p < 0.01) return p.toFixed(6);
if (p < 1) return p.toFixed(4);
return p.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
async function loadMarket() {
try {
const res = await fetch('api.php?action=market_data');
const data = await res.json();
const tbody = document.getElementById('market-tbody');
let html = '';
data.slice(0, 10).forEach(coin => {
const changeClass = coin.change >= 0 ? 'text-success' : 'text-danger';
const pFormatted = formatPrice(coin.price);
if (coin.symbol === 'BTCUSDT') {
document.getElementById('hero-btc-price').textContent = '$' + pFormatted;
}
html += `
<tr style="cursor: pointer;" onclick="location.href='trade.php?symbol=${coin.symbol}'" class="border-bottom border-secondary">
<td class="ps-4 py-4">
<div class="d-flex align-items-center">
<img src="${coin.icon_url}" class="me-3 rounded-circle" style="width:32px; height:32px;" onerror="this.src='https://cryptologos.cc/logos/generic-coin-logo.png'">
<div>
<div class="fw-bold text-white fs-6">${coin.symbol.replace('USDT', '')}</div>
<div class="text-secondary smaller" style="font-size: 11px;">${coin.name}</div>
</div>
</div>
</td>
<td><span class="fw-bold fs-5 text-white">$ ${pFormatted}</span></td>
<td><span class="${changeClass} fw-bold">${coin.change >= 0 ? '+' : ''}${coin.change}%</span></td>
<td class="d-none d-md-table-cell">
<div class="text-success small" style="font-size: 11px;">H: ${formatPrice(coin.high || coin.price*1.01)}</div>
<div class="text-danger small" style="font-size: 11px;">L: ${formatPrice(coin.low || coin.price*0.99)}</div>
</td>
<td class="text-end pe-4">
<a href="trade.php?symbol=${coin.symbol}" class="btn btn-sm btn-warning px-4 fw-bold">交易</a>
</td>
</tr>
`;
});
tbody.innerHTML = html;
} catch (e) {
console.error('Market load failed', e);
}
}
loadMarket();
setInterval(loadMarket, 3000);
</script>
<?php include 'footer.php'; ?>

101
login.php Normal file
View File

@ -0,0 +1,101 @@
<?php
include_once 'config.php';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if ($username && $password) {
$stmt = db()->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
header("Location: index.php");
exit;
} else {
$error = '用户名或密码错误';
}
} else {
$error = '请填写所有字段';
}
}
include 'header.php';
?>
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%);">
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 450px; border-radius: 16px;">
<div class="text-center mb-5">
<div class="logo-circle mb-4 mx-auto">
<i class="bi bi-hexagon-fill text-warning display-4"></i>
</div>
<h2 class="fw-bold text-white">欢迎登录 <?php echo $project_name; ?></h2>
<p class="text-secondary">全球领先的加密资产交易平台</p>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<form method="post">
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">用户名</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
<i class="bi bi-person"></i>
</span>
<input type="text" name="username" class="form-control bg-transparent text-white border-secondary" placeholder="请输入用户名" required>
</div>
</div>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">密码</label>
<div class="input-group">
<span class="input-group-text bg-transparent border-secondary text-secondary">
<i class="bi bi-lock"></i>
</span>
<input type="password" name="password" class="form-control bg-transparent text-white border-secondary" placeholder="请输入密码" required>
</div>
</div>
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">登录</button>
</form>
<div class="text-center">
<p class="text-secondary small">还没有账户? <a href="/register.php" class="text-warning text-decoration-none fw-bold">立即注册</a></p>
</div>
</div>
</div>
<style>
.auth-card {
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
}
.input-group-text {
border-right: none;
}
input.form-control {
border-left: none;
padding: 12px;
}
input.form-control:focus {
background: rgba(255, 255, 255, 0.05) !important;
box-shadow: none;
border-color: var(--accent-color);
}
.logo-circle {
width: 80px;
height: 80px;
background: rgba(240, 185, 11, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
</style>
<?php include 'footer.php'; ?>

6
logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_destroy();
header("Location: login.php");
exit;
?>

192
market.php Normal file
View File

@ -0,0 +1,192 @@
<?php
include 'header.php';
?>
<div class="container py-5">
<!-- Market Trends Cards -->
<div class="row mb-5 g-4">
<div class="col-md-4">
<div class="glass-card p-4 h-100" style="background: linear-gradient(135deg, rgba(14, 203, 129, 0.1) 0%, rgba(24, 26, 32, 1) 100%);">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="text-secondary mb-0">今日涨幅榜</h6>
<i class="bi bi-graph-up-arrow text-success"></i>
</div>
<div id="top-gainers">
<div class="text-center py-3 text-secondary small">加载中...</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="glass-card p-4 h-100">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="text-secondary mb-0">新币上架</h6>
<i class="bi bi-fire text-warning"></i>
</div>
<div id="new-listings">
<div class="text-center py-3 text-secondary small">加载中...</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="glass-card p-4 h-100">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="text-secondary mb-0">成交热度榜</h6>
<i class="bi bi-lightning-fill text-info"></i>
</div>
<div id="top-volume">
<div class="text-center py-3 text-secondary small">加载中...</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between align-items-end mb-4">
<div>
<h2 class="text-white fw-bold">行情中心</h2>
<p class="text-secondary mb-0">实时监测全球主流数字货币市场价格波动</p>
</div>
<div class="input-group" style="width: 320px;">
<span class="input-group-text bg-dark border-secondary text-secondary"><i class="bi bi-search"></i></span>
<input type="text" id="market-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种名称 / 简称" onkeyup="filterMarket()">
</div>
</div>
<div class="glass-card shadow-lg border-secondary-subtle">
<div class="table-responsive">
<table class="table table-dark table-hover align-middle mb-0">
<thead>
<tr class="text-secondary border-bottom border-secondary" style="background: #1e2329;">
<th class="ps-4 py-3">币种名称</th>
<th>最新价格</th>
<th>24h 涨跌</th>
<th>24h 最高/最低</th>
<th>24h 成交额(USDT)</th>
<th class="text-end pe-4">操作</th>
</tr>
</thead>
<tbody id="full-market-tbody">
<tr><td colspan="6" class="text-center py-5"><div class="spinner-border text-warning"></div></td></tr>
</tbody>
</table>
</div>
</div>
<!-- Crypto News Section (Rich Content Enhancement) -->
<div class="mt-5">
<h4 class="text-white mb-4"><i class="bi bi-newspaper text-warning me-2"></i> 行业资讯</h4>
<div class="row g-4">
<div class="col-md-6">
<div class="glass-card p-4 h-100 d-flex gap-3 align-items-center">
<img src="https://public.bnbstatic.com/image/cms/article/body/202303/34e0e5e0-9e9b-4377-98e6-7b244c767e6c.png" style="width: 120px; border-radius: 8px;" class="d-none d-sm-block">
<div>
<span class="badge bg-primary mb-2">快讯</span>
<h6 class="text-white mb-2">以太坊坎昆升级顺利完成Layer 2 费用大幅下降</h6>
<p class="text-secondary smaller mb-0">此次升级引入了 Proto-Danksharding为以太坊扩容迈出关键一步...</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="glass-card p-4 h-100 d-flex gap-3 align-items-center">
<img src="https://public.bnbstatic.com/image/cms/article/body/202206/a27e7f6d0f8645089f2a2438883907e1.png" style="width: 120px; border-radius: 8px;" class="d-none d-sm-block">
<div>
<span class="badge bg-warning text-dark mb-2">深度</span>
<h6 class="text-white mb-2">比特币减半后市场走势分析:机遇还是挑战?</h6>
<p class="text-secondary smaller mb-0">历史数据显示,减半通常伴随着周期的转折,本次减半有何不同...</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let allMarketData = [];
function formatPrice(p) {
p = parseFloat(p);
if (p < 0.0001) return p.toFixed(8);
if (p < 0.01) return p.toFixed(6);
if (p < 1) return p.toFixed(4);
return p.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
async function refreshMarket() {
try {
const res = await fetch('api.php?action=market_data');
const data = await res.json();
allMarketData = data;
renderMarket();
renderTrends();
} catch (e) {
console.error('Market refresh failed', e);
}
}
function renderTrends() {
const gainers = [...allMarketData].sort((a, b) => b.change - a.change).slice(0, 3);
const volumes = [...allMarketData].sort((a, b) => b.volume - a.volume).slice(0, 3);
const news = allMarketData.slice(2, 5);
document.getElementById('top-gainers').innerHTML = gainers.map(c => `
<div class="d-flex justify-content-between mb-2 align-items-center">
<span class="text-white small fw-bold">${c.symbol.replace('USDT','')}</span>
<span class="text-success small fw-bold">+${c.change}%</span>
</div>
`).join('');
document.getElementById('new-listings').innerHTML = news.map(c => `
<div class="d-flex justify-content-between mb-2 align-items-center">
<span class="text-white small fw-bold">${c.symbol.replace('USDT','')}</span>
<span class="text-warning small" style="font-size: 10px;">HOT</span>
</div>
`).join('');
document.getElementById('top-volume').innerHTML = volumes.map(c => `
<div class="d-flex justify-content-between mb-2 align-items-center">
<span class="text-white small fw-bold">${c.symbol.replace('USDT','')}</span>
<span class="text-secondary smaller">${(c.volume/1000000).toFixed(1)}M</span>
</div>
`).join('');
}
function renderMarket() {
const search = document.getElementById('market-search').value.toLowerCase();
let html = '';
allMarketData.forEach(coin => {
if (search && !coin.symbol.toLowerCase().includes(search) && !coin.name.toLowerCase().includes(search)) return;
const changeClass = coin.change >= 0 ? 'text-success' : 'text-danger';
const pFormatted = formatPrice(coin.price);
html += `
<tr onclick="location.href='trade.php?symbol=${coin.symbol}'" style="cursor:pointer" class="border-bottom border-secondary">
<td class="ps-4 py-3">
<div class="d-flex align-items-center">
<img src="${coin.icon_url}" class="me-3 rounded-circle" width="28" height="28" onerror="this.src='https://cryptologos.cc/logos/generic-coin-logo.png'">
<div>
<span class="fw-bold text-white d-block">${coin.symbol.replace('USDT', '')}</span>
<span class="text-secondary smaller" style="font-size: 10px;">${coin.name}</span>
</div>
</div>
</td>
<td class="fw-bold fs-6">$ ${pFormatted}</td>
<td class="${changeClass} fw-bold">${coin.change >= 0 ? '+' : ''}${coin.change}%</td>
<td>
<div class="text-success" style="font-size: 11px;">H: ${formatPrice(coin.high || coin.price * 1.01)}</div>
<div class="text-danger" style="font-size: 11px;">L: ${formatPrice(coin.low || coin.price * 0.99)}</div>
</td>
<td class="text-secondary smaller">${parseFloat(coin.volume || 0).toLocaleString()}</td>
<td class="text-end pe-4">
<a href="trade.php?symbol=${coin.symbol}" class="btn btn-sm btn-outline-warning fw-bold px-3 py-1" style="font-size: 12px;">交易</a>
</td>
</tr>
`;
});
document.getElementById('full-market-tbody').innerHTML = html;
}
function filterMarket() { renderMarket(); }
refreshMarket();
setInterval(refreshMarket, 3000);
</script>
<?php include 'footer.php'; ?>

221
profile.php Normal file
View File

@ -0,0 +1,221 @@
<?php
include_once 'config.php';
check_auth();
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
// Fetch assets
$stmt = db()->prepare("SELECT * FROM assets WHERE account_id = ? AND balance > 0");
$stmt->execute([$account['id']]);
$assets = $stmt->fetchAll();
include 'header.php';
?>
<div class="container py-5">
<div class="row">
<!-- User Sidebar -->
<div class="col-lg-4">
<div class="glass-card p-4 mb-4 text-center">
<div class="position-relative d-inline-block mb-3">
<i class="bi bi-person-circle text-warning" style="font-size: 80px;"></i>
<?php if($account['kyc_status'] == 'VERIFIED'): ?>
<span class="position-absolute bottom-0 end-0 bg-success rounded-circle p-1" style="border: 4px solid var(--bg-card);">
<i class="bi bi-check-lg text-white" style="font-size: 14px;"></i>
</span>
<?php endif; ?>
</div>
<h4 class="text-white mb-1"><?php echo htmlspecialchars($_SESSION['username']); ?></h4>
<div class="d-flex justify-content-center gap-2 mb-4">
<span class="badge bg-dark border border-secondary text-secondary px-3 py-2">UID: <?php echo $account['uid']; ?></span>
<span class="badge bg-<?php echo $account['kyc_status']=='VERIFIED'?'success':'warning'; ?> text-<?php echo $account['kyc_status']=='VERIFIED'?'white':'dark'; ?> px-3 py-2">
<?php
$kyc_labels = ['UNVERIFIED'=>'未认证', 'PENDING'=>'审核中', 'VERIFIED'=>'已认证', 'REJECTED'=>'已驳回'];
echo $kyc_labels[$account['kyc_status']] ?? '未认证';
?>
</span>
</div>
<div class="list-group list-group-flush bg-transparent text-start">
<a href="verify.php" class="list-group-item bg-transparent text-secondary border-secondary d-flex justify-content-between px-0 py-3">
<span><i class="bi bi-shield-check me-2"></i> 实名认证</span>
<span class="<?php echo $account['kyc_status']=='VERIFIED'?'text-success':'text-warning'; ?> small">
<?php echo $account['kyc_status'] == 'UNVERIFIED' ? '去认证 <i class="bi bi-chevron-right"></i>' : $kyc_labels[$account['kyc_status']]; ?>
</span>
</a>
<div class="list-group-item bg-transparent text-secondary border-secondary d-flex justify-content-between px-0 py-3">
<span><i class="bi bi-star me-2"></i> 信用评分</span>
<span class="text-white fw-bold"><?php echo $account['credit_score']; ?></span>
</div>
<div class="list-group-item bg-transparent text-secondary border-secondary d-flex justify-content-between px-0 py-3">
<span><i class="bi bi-calendar3 me-2"></i> 注册时间</span>
<span class="text-white small"><?php echo substr($account['created_at'], 0, 10); ?></span>
</div>
</div>
<a href="logout.php" class="btn btn-outline-danger w-100 mt-4 border-0" style="background: rgba(220, 53, 69, 0.1);">安全退出</a>
</div>
<div class="glass-card p-4 mb-4">
<h6 class="text-white mb-3">安全中心</h6>
<div class="d-flex align-items-center mb-3">
<div class="bg-success rounded-circle p-2 me-3" style="width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;">
<i class="bi bi-envelope text-white small"></i>
</div>
<div>
<div class="text-white small">绑定邮箱</div>
<div class="text-secondary smaller"><?php echo substr($_SESSION['username'], 0, 3); ?>***@gmail.com</div>
</div>
</div>
<div class="d-flex align-items-center">
<div class="bg-warning rounded-circle p-2 me-3" style="width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;">
<i class="bi bi-phone text-dark small"></i>
</div>
<div>
<div class="text-white small">绑定手机</div>
<div class="text-warning smaller" style="cursor: pointer;">未绑定,去绑定 ></div>
</div>
</div>
</div>
</div>
<!-- Asset Content -->
<div class="col-lg-8">
<!-- Balance Card -->
<div class="glass-card p-4 mb-4" style="background: linear-gradient(135deg, #2b2f36 0%, #181a20 100%); border: 1px solid #fcd53533; box-shadow: 0 10px 30px rgba(0,0,0,0.5);">
<div class="row align-items-center">
<div class="col-md-7">
<div class="text-secondary mb-2 small fw-bold">账户总资产 (估算)</div>
<h2 class="text-white fw-bold mb-1">
<span class="text-warning">$</span> <?php echo number_format($account['balance'] + $account['frozen_balance'], 2); ?> <span class="fs-6 text-secondary fw-normal">USDT</span>
</h2>
<div class="text-secondary smaller"> <?php echo number_format(($account['balance'] + $account['frozen_balance']) * 7.25, 2); ?> CNY</div>
</div>
<div class="col-md-5 text-md-end mt-3 mt-md-0">
<a href="deposit.php" class="btn btn-warning fw-bold px-4 me-2">充值</a>
<a href="withdraw.php" class="btn btn-outline-light fw-bold px-4">提现</a>
</div>
</div>
</div>
<!-- Asset Tabs -->
<div class="glass-card p-4 mb-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5 class="text-white mb-0"><i class="bi bi-wallet2 text-warning me-2"></i> 资产概览</h5>
<div class="form-check form-switch small">
<input class="form-check-input" type="checkbox" id="hideZero">
<label class="form-check-label text-secondary" for="hideZero">隐藏 0 余额</label>
</div>
</div>
<div class="table-responsive">
<table class="table table-dark table-hover align-middle">
<thead>
<tr class="text-secondary small border-bottom border-secondary">
<th class="ps-0">币种</th>
<th>总额</th>
<th>可用</th>
<th>冻结</th>
<th class="text-end pe-0">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ps-0">
<div class="d-flex align-items-center">
<div class="bg-success rounded-circle me-2 d-flex align-items-center justify-content-center" style="width:28px; height:28px;">
<i class="bi bi-currency-dollar text-white"></i>
</div>
<span class="fw-bold">USDT</span>
</div>
</td>
<td class="text-white fw-bold"><?php echo number_format($account['balance'] + $account['frozen_balance'], 2); ?></td>
<td class="text-white"><?php echo number_format($account['balance'], 2); ?></td>
<td class="text-secondary"><?php echo number_format($account['frozen_balance'], 2); ?></td>
<td class="text-end pe-0">
<a href="trade.php" class="btn btn-sm btn-link text-warning text-decoration-none">交易</a>
<a href="withdraw.php" class="btn btn-sm btn-link text-secondary text-decoration-none ms-2">提现</a>
</td>
</tr>
<?php foreach ($assets as $asset): if($asset['currency'] == 'USDT') continue; ?>
<tr>
<td class="ps-0">
<div class="d-flex align-items-center">
<div class="bg-secondary rounded-circle me-2 d-flex align-items-center justify-content-center fw-bold" style="width:28px; height:28px; font-size: 10px;">
<?php echo substr($asset['currency'], 0, 1); ?>
</div>
<span class="fw-bold"><?php echo $asset['currency']; ?></span>
</div>
</td>
<td class="text-white fw-bold"><?php echo number_format($asset['balance'] + $asset['frozen'], 6); ?></td>
<td class="text-white"><?php echo number_format($asset['balance'], 6); ?></td>
<td class="text-secondary"><?php echo number_format($asset['frozen'], 6); ?></td>
<td class="text-end pe-0">
<a href="trade.php?symbol=<?php echo $asset['currency']; ?>USDT" class="btn btn-sm btn-link text-warning text-decoration-none">交易</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<!-- Transaction History -->
<div class="glass-card p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5 class="text-white mb-0"><i class="bi bi-clock-history text-warning me-2"></i> 最近充提</h5>
<a href="#" class="text-warning small text-decoration-none">查看更多记录</a>
</div>
<div class="table-responsive">
<table class="table table-dark small align-middle">
<thead>
<tr class="text-secondary border-bottom border-secondary">
<th>时间</th>
<th>类型</th>
<th>金额</th>
<th>方式</th>
<th class="text-end">状态</th>
</tr>
</thead>
<tbody>
<?php
$stmt = db()->prepare("SELECT * FROM transactions WHERE account_id = ? ORDER BY timestamp DESC LIMIT 10");
$stmt->execute([$account['id']]);
$txs = $stmt->fetchAll();
if (empty($txs)):
?>
<tr><td colspan="5" class="text-center text-secondary py-5">暂无流水记录</td></tr>
<?php else: foreach($txs as $tx): ?>
<tr>
<td class="text-secondary"><?php echo substr($tx['timestamp'], 2, 14); ?></td>
<td>
<span class="badge bg-<?php echo $tx['transaction_type']=='deposit'?'success-subtle text-success':'danger-subtle text-danger'; ?> border border-<?php echo $tx['transaction_type']=='deposit'?'success':'danger'; ?> px-2 py-1">
<?php echo $tx['transaction_type']=='deposit'?'充值':'提现'; ?>
</span>
</td>
<td class="fw-bold"><?php echo number_format($tx['amount'], 2); ?> <span class="smaller fw-normal">USDT</span></td>
<td class="text-secondary"><?php echo $tx['pay_method'] ?? 'USDT'; ?></td>
<td class="text-end">
<?php if($tx['status'] == 'completed'): ?>
<span class="text-success"><i class="bi bi-check-circle me-1"></i>已成功</span>
<?php elseif($tx['status'] == 'failed'): ?>
<span class="text-danger"><i class="bi bi-x-circle me-1"></i>已驳回</span>
<?php else: ?>
<span class="text-warning"><i class="bi bi-hourglass-split me-1"></i>审核中</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<style>
.bg-success-subtle { background-color: rgba(14, 203, 129, 0.1) !important; }
.bg-danger-subtle { background-color: rgba(246, 70, 93, 0.1) !important; }
.smaller { font-size: 0.75rem; }
</style>
<?php include 'footer.php'; ?>

88
register.php Normal file
View File

@ -0,0 +1,88 @@
<?php
include_once 'config.php';
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
$confirm_password = $_POST['confirm_password'] ?? '';
if ($username && $password && $confirm_password) {
if ($password !== $confirm_password) {
$error = '两次输入的密码不一致';
} else {
$db = db();
$stmt = $db->prepare("SELECT id FROM users WHERE username = ?");
$stmt->execute([$username]);
if ($stmt->fetch()) {
$error = '用户名已存在';
} else {
try {
$db->beginTransaction();
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $db->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->execute([$username, $hashed_password]);
$user_id = $db->lastInsertId();
$uid = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
$stmt = $db->prepare("INSERT INTO accounts (user_id, uid, balance) VALUES (?, ?, ?)");
$stmt->execute([$user_id, $uid, 10000.00]); // Default 10000 USDT for simulated
$db->commit();
$success = '注册成功!正在跳转到登录页...';
header("Refresh: 2; URL=login.php");
} catch (Exception $e) {
$db->rollBack();
$error = '注册失败: ' . $e->getMessage();
}
}
}
} else {
$error = '请填写所有字段';
}
}
include 'header.php';
?>
<div class="auth-container d-flex align-items-center justify-content-center" style="min-height: 90vh; background: radial-gradient(circle at top right, #1e2329 0%, #0b0e11 100%);">
<div class="auth-card glass-card p-5" style="width: 100%; max-width: 450px; border-radius: 16px;">
<div class="text-center mb-5">
<h2 class="fw-bold text-white">创建账户</h2>
<p class="text-secondary">加入全球领先的交易社区</p>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<form method="post">
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">用户名</label>
<input type="text" name="username" class="form-control bg-transparent text-white border-secondary" placeholder="请输入用户名" required>
</div>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">密码</label>
<input type="password" name="password" class="form-control bg-transparent text-white border-secondary" placeholder="请输入密码" required>
</div>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold">确认密码</label>
<input type="password" name="confirm_password" class="form-control bg-transparent text-white border-secondary" placeholder="请再次输入密码" required>
</div>
<button type="submit" class="btn btn-warning w-100 py-3 fw-bold shadow-lg mb-4">立即注册</button>
</form>
<div class="text-center">
<p class="text-secondary small">已有账户? <a href="/login.php" class="text-warning text-decoration-none fw-bold">立即登录</a></p>
</div>
</div>
</div>
<?php include 'footer.php'; ?>

View File

@ -1,3 +1,30 @@
asgiref==3.10.0
certifi==2022.9.24
chardet==5.1.0
charset-normalizer==3.0.1
dbus-python==1.3.2
distro-info==1.5+deb12u1
Django==5.2.7
httplib2==0.20.4
idna==3.3
markdown-it-py==2.1.0
mdurl==0.1.2
mysqlclient==2.2.7
netifaces==0.11.0
pycurl==7.45.2
Pygments==2.14.0
PyGObject==3.42.2
pyparsing==3.0.9
PySimpleSOAP==1.16.2
python-apt==2.6.0
python-debian==0.1.49
python-debianbts==4.0.1
python-dotenv==1.1.1
PyYAML==6.0
reportbug==12.0.0
requests==2.28.1
rich==13.3.1
six==1.16.0
sqlparse==0.5.3
unattended-upgrades==0.1
urllib3==1.26.12

45
reset_admin.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* 管理员密码重置工具
* 使用方法:上传到宝塔根目录,访问:域名/reset_admin.php
* 注意:使用后请务必删除此文件以保安全!
*/
require_once 'config.php';
$new_username = 'admin';
$new_password = 'admin123'; // 您可以将此处修改为您想要的密码
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
try {
$db = db();
// 检查表是否存在
$db->exec("CREATE TABLE IF NOT EXISTS admins (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(150) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
// 更新或插入管理员
$stmt = $db->prepare("SELECT id FROM admins WHERE username = ?");
$stmt->execute([$new_username]);
if ($stmt->fetch()) {
$stmt = $db->prepare("UPDATE admins SET password = ? WHERE username = ?");
$stmt->execute([$hashed_password, $new_username]);
} else {
$stmt = $db->prepare("INSERT INTO admins (username, password) VALUES (?, ?)");
$stmt->execute([$new_username, $hashed_password]);
}
echo "<h3>管理员账号重置成功!</h3>";
echo "<p>用户名: <b>$new_username</b></p>";
echo "<p>新密码: <b>$new_password</b></p>";
echo "<p style='color:red;'><b>警告:请立即删除 reset_admin.php 文件!</b></p>";
echo "<a href='admin_login.php'>前往登录</a>";
} catch (Exception $e) {
echo "<h3>重置失败:</h3>";
echo "<p>" . $e->getMessage() . "</p>";
echo "<p>请检查 db/config.php 中的数据库连接配置是否正确。</p>";
}

114
schema.sql Normal file
View File

@ -0,0 +1,114 @@
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(150) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS admins (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(150) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS accounts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
uid VARCHAR(6) UNIQUE NOT NULL,
account_type ENUM('SIMULATED', 'REAL') DEFAULT 'SIMULATED',
balance DECIMAL(30, 8) DEFAULT 0,
frozen_balance DECIMAL(30, 8) DEFAULT 0,
credit_score INT DEFAULT 80,
kyc_status ENUM('UNVERIFIED', 'PENDING', 'VERIFIED', 'REJECTED') DEFAULT 'UNVERIFIED',
win_loss_control INT DEFAULT 0, -- 1: Always Win, -1: Always Loss, 0: Normal
language VARCHAR(10) DEFAULT 'zh-hans',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS site_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
site_name VARCHAR(100) DEFAULT 'BitCrypto',
contact_email VARCHAR(100) DEFAULT 'support@example.com',
deposit_address VARCHAR(255) DEFAULT 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
customer_service_url TEXT,
terms_content TEXT,
privacy_content TEXT,
is_pinning_active BOOLEAN DEFAULT FALSE
);
CREATE TABLE IF NOT EXISTS cryptocurrencies (
id INT AUTO_INCREMENT PRIMARY KEY,
symbol VARCHAR(20) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
icon_url TEXT,
current_price DECIMAL(30, 8) DEFAULT 0,
manual_price DECIMAL(30, 8) DEFAULT 0,
change_24h DECIMAL(10, 2) DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE IF NOT EXISTS assets (
id INT AUTO_INCREMENT PRIMARY KEY,
account_id INT NOT NULL,
currency VARCHAR(10) NOT NULL,
balance DECIMAL(30, 8) DEFAULT 0,
frozen DECIMAL(30, 8) DEFAULT 0,
UNIQUE KEY account_currency (account_id, currency),
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS orders (
id INT AUTO_INCREMENT PRIMARY KEY,
account_id INT NOT NULL,
symbol VARCHAR(20) DEFAULT 'BTCUSDT',
trade_type ENUM('SPOT', 'CONTRACT') DEFAULT 'SPOT',
side ENUM('BUY', 'SELL') NOT NULL,
order_type ENUM('LIMIT', 'MARKET') NOT NULL,
price DECIMAL(30, 8),
amount DECIMAL(30, 8) NOT NULL,
total_usdt DECIMAL(30, 8),
leverage INT DEFAULT 1,
status ENUM('PENDING', 'PARTIALLY_FILLED', 'FILLED', 'CANCELED') DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS positions (
id INT AUTO_INCREMENT PRIMARY KEY,
account_id INT NOT NULL,
symbol VARCHAR(20) NOT NULL,
side ENUM('LONG', 'SHORT') NOT NULL,
leverage INT DEFAULT 20,
entry_price DECIMAL(30, 8) NOT NULL,
lots DECIMAL(30, 8) NOT NULL,
margin DECIMAL(30, 8) NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
account_id INT NOT NULL,
transaction_type ENUM('deposit', 'withdraw') NOT NULL,
currency VARCHAR(10) DEFAULT 'USDT',
amount DECIMAL(30, 8) DEFAULT 0,
tx_hash VARCHAR(255),
status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
-- Seed initial data
INSERT INTO site_settings (site_name, contact_email, deposit_address) VALUES ('BitCrypto', 'support@bitcrypto.com', 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t');
INSERT INTO admins (username, password) VALUES ('admin', '$2y$10$vK6.O/M57M.n5oYvT6pXve/tE6Yk.7Zg8XfVv0VzP2/k1e7Y6oM5e'); -- password: admin
INSERT INTO cryptocurrencies (symbol, name, icon_url, current_price, change_24h) VALUES
('BTCUSDT', 'Bitcoin', 'https://cryptologos.cc/logos/bitcoin-btc-logo.png', 45000.00, 1.2),
('ETHUSDT', 'Ethereum', 'https://cryptologos.cc/logos/ethereum-eth-logo.png', 2500.00, -0.5),
('BNBUSDT', 'Binance Coin', 'https://cryptologos.cc/logos/binance-coin-bnb-logo.png', 300.00, 2.1),
('ADAUSDT', 'Cardano', 'https://cryptologos.cc/logos/cardano-ada-logo.png', 0.5, 3.5),
('SOLUSDT', 'Solana', 'https://cryptologos.cc/logos/solana-sol-logo.png', 100.0, 5.0),
('DOGEUSDT', 'Dogecoin', 'https://cryptologos.cc/logos/dogecoin-doge-logo.png', 0.08, -2.0);

View File

@ -1,4 +1,162 @@
/* Custom styles for the application */
body {
font-family: system-ui, -apple-system, sans-serif;
:root {
--bg-dark: #0a0e17;
--bg-card: #161a25;
--bg-card-hover: #1e2330;
--text-primary: #ffffff;
--text-secondary: #848e9c;
--accent-color: #f0b90b;
--accent-hover: #d4a30a;
--up-color: #0ecb81;
--down-color: #f6465d;
--border-color: #2b2f36;
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.1);
}
body {
background-color: var(--bg-dark);
color: var(--text-primary);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
overflow-x: hidden;
}
/* Glassmorphism */
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 16px;
}
/* Animations */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes float {
0% { transform: translatey(0px); }
50% { transform: translatey(-20px); }
100% { transform: translatey(0px); }
}
.animate-up { animation: fadeInUp 0.8s ease-out forwards; }
.float-icon { animation: float 6s ease-in-out infinite; }
/* Navbar */
.navbar {
background: rgba(10, 14, 23, 0.8) !important;
backdrop-filter: blur(15px);
border-bottom: 1px solid var(--border-color);
}
/* Hero Section */
.hero-section {
padding: 100px 0;
background: radial-gradient(circle at top right, rgba(240, 185, 11, 0.1), transparent 40%),
radial-gradient(circle at bottom left, rgba(14, 203, 129, 0.1), transparent 40%);
}
.hero-title {
font-size: 4rem;
font-weight: 800;
line-height: 1.2;
margin-bottom: 24px;
background: linear-gradient(135deg, #fff 0%, #848e9c 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Markets Table */
.market-table {
background: var(--bg-card);
border-radius: 16px;
overflow: hidden;
border: 1px solid var(--border-color);
}
.market-table th {
color: var(--text-secondary);
font-weight: 500;
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
}
.market-table td {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
vertical-align: middle;
}
.market-table tr:hover {
background-color: var(--bg-card-hover);
}
/* Buttons */
.btn-primary-custom {
background: var(--accent-color);
color: #000;
border: none;
padding: 12px 32px;
border-radius: 8px;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary-custom:hover {
background: var(--accent-hover);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(240, 185, 11, 0.3);
}
/* Carousel / Banners */
.banner-carousel {
border-radius: 20px;
overflow: hidden;
}
.banner-item {
height: 300px;
background-size: cover;
background-position: center;
display: flex;
align-items: center;
padding: 40px;
}
/* Trading Interface */
.trade-container {
display: grid;
grid-template-columns: 1fr 300px 300px;
grid-template-rows: 1fr 300px;
gap: 8px;
height: calc(100vh - 70px);
padding: 8px;
background: #000;
}
.trade-panel {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 4px;
overflow: hidden;
}
.price-up { color: var(--up-color); }
.price-down { color: var(--down-color); }
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}

BIN
static/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

371
trade.php Normal file
View File

@ -0,0 +1,371 @@
<?php
include_once 'config.php';
check_auth();
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
$symbol = $_GET['symbol'] ?? 'BTCUSDT';
$trade_type = $_GET['type'] ?? 'SPOT';
$base_symbol = str_replace('USDT', '', $symbol);
include 'header.php';
?>
<style>
.glass-card { background: rgba(24, 26, 32, 1); border: 1px solid #2b2f36; border-radius: 4px; overflow: hidden; }
.trade-nav-item { cursor: pointer; padding: 10px 15px; border-bottom: 2px solid transparent; color: #848e9c; font-size: 14px; }
.trade-nav-item.active { border-bottom-color: #f0b90b; color: #f0b90b; font-weight: bold; }
.coin-row { transition: background 0.2s; border-bottom: 1px solid #1e2329; }
.coin-row:hover { background: #2b3139; cursor: pointer; }
.coin-row.active { background: #1e2329; border-left: 3px solid #f0b90b; }
.price-up { color: #0ecb81 !important; }
.price-down { color: #f6465d !important; }
#order-book table td { padding: 3px 8px; font-size: 12px; }
.smaller { font-size: 11px; }
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-thumb { background: #3b4149; border-radius: 10px; }
</style>
<div class="container-fluid px-1 py-1" style="background-color: #0b0e11; min-height: 95vh; color: #eaecef;">
<div class="row g-1">
<!-- Market List -->
<div class="col-lg-2">
<div class="glass-card h-100" style="max-height: 900px; display: flex; flex-direction: column;">
<div class="p-2 border-bottom border-secondary bg-dark">
<div class="input-group input-group-sm">
<span class="input-group-text bg-transparent border-secondary text-secondary"><i class="bi bi-search"></i></span>
<input type="text" id="coin-search" class="form-control bg-transparent text-white border-secondary" placeholder="搜索币种" onkeyup="filterCoins()">
</div>
</div>
<div id="left-coin-list" style="overflow-y: auto; flex-grow: 1;">
<!-- JS populated -->
</div>
</div>
</div>
<!-- Chart & Trade -->
<div class="col-lg-7">
<!-- Ticker Header -->
<div class="glass-card mb-1 p-2 d-flex align-items-center bg-dark">
<div class="d-flex align-items-center me-4">
<span class="fw-bold fs-5 text-white" id="current-symbol-title"><?php echo $symbol; ?></span>
</div>
<div class="me-4 border-end border-secondary pe-4">
<div class="fw-bold fs-5" id="header-price">--</div>
<div class="smaller text-secondary"> $<span id="header-price-usd">--</span></div>
</div>
<div class="me-4">
<div class="smaller text-secondary mb-1">24h 涨跌</div>
<div class="fw-bold" id="header-change">--</div>
</div>
<div class="me-4 d-none d-md-block">
<div class="smaller text-secondary mb-1">24h 最高</div>
<div class="fw-bold smaller" id="header-high">--</div>
</div>
<div class="me-4 d-none d-md-block">
<div class="smaller text-secondary mb-1">24h 最低</div>
<div class="fw-bold smaller" id="header-low">--</div>
</div>
<div class="ms-auto d-flex gap-1 bg-black p-1 rounded">
<a href="?type=SPOT&symbol=<?php echo $symbol; ?>" class="btn btn-sm <?php echo $trade_type=='SPOT'?'btn-warning':'text-secondary'; ?> fw-bold px-3">现货</a>
<a href="?type=CONTRACT&symbol=<?php echo $symbol; ?>" class="btn btn-sm <?php echo $trade_type=='CONTRACT'?'btn-warning':'text-secondary'; ?> fw-bold px-3">合约</a>
</div>
</div>
<!-- TradingView -->
<div class="glass-card mb-1" style="height: 480px;">
<div id="tradingview_widget" style="height: 100%;"></div>
</div>
<!-- Trading Form -->
<div class="glass-card p-3">
<div class="row g-4">
<div class="col-md-6 border-end border-secondary">
<div class="d-flex justify-content-between mb-3 align-items-center">
<span class="text-success fw-bold fs-5">买入 / 做多</span>
<span class="text-secondary smaller">可用: <span class="text-white" id="usdt-balance"><?php echo number_format($account['balance'], 2); ?></span> USDT</span>
</div>
<?php if ($trade_type === 'CONTRACT'): ?>
<div class="mb-3 d-flex align-items-center gap-2">
<label class="smaller text-secondary" style="min-width: 40px;">杠杆</label>
<select id="leverage" class="form-select form-select-sm bg-dark text-white border-secondary w-auto" style="min-width: 100px;">
<option value="10">10x</option>
<option value="20" selected>20x</option>
<option value="50">50x</option>
<option value="100">100x</option>
</select>
</div>
<?php endif; ?>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;">价格</span>
<input type="text" class="form-control bg-dark text-warning border-secondary fw-bold" value="市场最优价" disabled>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;"><?php echo $trade_type=='CONTRACT'?'手数':'数量'; ?></span>
<input type="number" id="buy-amount" class="form-control bg-dark text-white border-secondary" placeholder="0.00">
<span class="input-group-text bg-dark text-secondary border-secondary"><?php echo $trade_type=='CONTRACT'?'张':$base_symbol; ?></span>
</div>
<button class="btn btn-success w-100 fw-bold py-2 shadow-sm" onclick="submitOrder('BUY')">立即买入 (做多)</button>
</div>
<div class="col-md-6">
<div class="d-flex justify-content-between mb-3 align-items-center">
<span class="text-danger fw-bold fs-5">卖出 / 做空</span>
<span class="text-secondary smaller">市价委托</span>
</div>
<?php if ($trade_type === 'CONTRACT'): ?>
<div class="mb-3"><div style="height:31px"></div></div>
<?php endif; ?>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;">价格</span>
<input type="text" class="form-control bg-dark text-warning border-secondary fw-bold" value="市场最优价" disabled>
</div>
<div class="input-group input-group-sm mb-3">
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 60px;"><?php echo $trade_type=='CONTRACT'?'手数':'数量'; ?></span>
<input type="number" id="sell-amount" class="form-control bg-dark text-white border-secondary" placeholder="0.00">
<span class="input-group-text bg-dark text-secondary border-secondary"><?php echo $trade_type=='CONTRACT'?'张':$base_symbol; ?></span>
</div>
<button class="btn btn-danger w-100 fw-bold py-2 shadow-sm" onclick="submitOrder('SELL')">立即卖出 (做空)</button>
</div>
</div>
</div>
<!-- Positions & History -->
<div class="glass-card mt-1 p-0">
<div class="d-flex border-bottom border-secondary bg-dark">
<div class="trade-nav-item active">当前持有仓位</div>
<div class="trade-nav-item">历史成交记录</div>
</div>
<div class="p-0" style="min-height: 250px;">
<div class="table-responsive">
<table class="table table-dark table-hover mb-0 align-middle" id="position-table" style="font-size: 13px;">
<thead>
<tr class="text-secondary" style="background: #1e2329;">
<th class="ps-3">交易对</th>
<th>方向</th>
<th>杠杆</th>
<th>持有数量</th>
<th>开仓价格</th>
<th>标记价格</th>
<th>盈亏 (USDT)</th>
<th class="text-end pe-3">操作</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Order Book -->
<div class="col-lg-3">
<div class="glass-card h-100" style="display: flex; flex-direction: column;">
<div class="p-2 border-bottom border-secondary bg-dark small fw-bold">实时订单簿</div>
<div id="order-book" style="flex-grow: 1;">
<div class="px-2 py-2 d-flex justify-content-between text-secondary smaller">
<span>价格 (USDT)</span>
<span>数量 (<?php echo $base_symbol; ?>)</span>
</div>
<div id="asks-container" class="mb-1">
<table class="w-100">
<tbody id="asks-list"></tbody>
</table>
</div>
<div class="py-3 text-center border-top border-bottom border-secondary my-1 bg-dark">
<span id="book-price" class="fs-4 fw-bold">--</span>
<div class="smaller text-secondary" id="book-price-usd"> $--</div>
</div>
<div id="bids-container">
<table class="w-100">
<tbody id="bids-list"></tbody>
</table>
</div>
</div>
<div class="p-2 border-top border-secondary bg-dark">
<div class="d-flex justify-content-between smaller">
<span class="text-secondary">最新成交</span>
<span class="text-success">实时</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script>
const symbol = '<?php echo $symbol; ?>';
const tradeType = '<?php echo $trade_type; ?>';
let currentPrice = 0;
let allCoins = [];
new TradingView.widget({
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
"interval": "15", "timezone": "Etc/UTC", "theme": "dark", "style": "1",
"locale": "zh_CN", "toolbar_bg": "#f1f3f6", "enable_publishing": false,
"hide_side_toolbar": false, "allow_symbol_change": true, "container_id": "tradingview_widget"
});
function formatPrice(p) {
p = parseFloat(p);
if (p < 0.0001) return p.toFixed(8);
if (p < 0.01) return p.toFixed(6);
if (p < 1) return p.toFixed(4);
return p.toFixed(2);
}
async function updateMarket() {
try {
const r = await fetch('api.php?action=market_data');
const coins = await r.json();
allCoins = coins;
renderCoins();
const c = coins.find(i => i.symbol === symbol);
if (c) {
currentPrice = parseFloat(c.price);
const pFormatted = formatPrice(currentPrice);
const changeClass = c.change >= 0 ? 'price-up' : 'price-down';
document.getElementById('header-price').textContent = pFormatted;
document.getElementById('header-price').className = 'fw-bold fs-5 ' + changeClass;
document.getElementById('header-price-usd').textContent = pFormatted;
document.getElementById('header-change').textContent = (c.change >= 0 ? '+' : '') + c.change + '%';
document.getElementById('header-change').className = 'fw-bold ' + changeClass;
document.getElementById('header-high').textContent = formatPrice(c.high || currentPrice * 1.02);
document.getElementById('header-low').textContent = formatPrice(c.low || currentPrice * 0.98);
const bookPrice = document.getElementById('book-price');
bookPrice.textContent = pFormatted;
bookPrice.className = 'fs-4 fw-bold ' + changeClass;
document.getElementById('book-price-usd').textContent = '≈ $' + pFormatted;
renderOrderBook(currentPrice);
}
} catch(e) { console.error(e); }
}
function renderCoins() {
const search = document.getElementById('coin-search').value.toLowerCase();
let listHtml = '';
allCoins.forEach(c => {
if (search && !c.symbol.toLowerCase().includes(search)) return;
const isTarget = c.symbol === symbol;
const changeClass = c.change >= 0 ? 'text-success' : 'text-danger';
listHtml += `
<div class="coin-row p-2 d-flex justify-content-between align-items-center ${isTarget?'active':''}" onclick="location.href='?type=${tradeType}&symbol=${c.symbol}'">
<div class="d-flex align-items-center">
<img src="${c.icon_url}" width="20" height="20" class="me-2 rounded-circle">
<div>
<div class="fw-bold smaller text-white">${c.symbol.replace('USDT','')}</div>
<div class="text-secondary smaller" style="font-size:9px">USDT</div>
</div>
</div>
<div class="text-end">
<div class="fw-bold smaller ${changeClass}">${formatPrice(c.price)}</div>
<div class="${changeClass}" style="font-size:10px">${(c.change >= 0 ? '+' : '') + c.change}%</div>
</div>
</div>
`;
});
document.getElementById('left-coin-list').innerHTML = listHtml;
}
function filterCoins() { renderCoins(); }
function renderOrderBook(price) {
if (!price) return;
let asks = '', bids = '';
// Asks (Red) - from high to low
for(let i=10; i>0; i--) {
const p = price * (1 + i * 0.0001);
const amt = (Math.random() * 2).toFixed(3);
asks += `<tr><td class="text-danger">${formatPrice(p)}</td><td class="text-end text-secondary">${amt}</td></tr>`;
}
// Bids (Green) - from high to low
for(let i=1; i<=10; i++) {
const p = price * (1 - i * 0.0001);
const amt = (Math.random() * 2).toFixed(3);
bids += `<tr><td class="text-success">${formatPrice(p)}</td><td class="text-end text-secondary">${amt}</td></tr>`;
}
document.getElementById('asks-list').innerHTML = asks;
document.getElementById('bids-list').innerHTML = bids;
}
async function updatePositions() {
try {
const r = await fetch('api.php?action=positions');
const pos = await r.json();
let html = '';
if (pos.length === 0) {
html = '<tr><td colspan="8" class="text-center py-5 text-secondary">暂无持有仓位</td></tr>';
} else {
pos.forEach(p => {
const pnlClass = p.pnl >= 0 ? 'text-success' : 'text-danger';
html += `
<tr>
<td class="ps-3 fw-bold">${p.symbol}</td>
<td><span class="badge ${p.side==='LONG'?'bg-success':'bg-danger'}">${p.side}</span></td>
<td><span class="text-warning">${p.leverage}x</span></td>
<td>${p.lots} </td>
<td>${formatPrice(p.entry_price)}</td>
<td class="text-warning">${formatPrice(p.current_price)}</td>
<td class="${pnlClass} fw-bold">${parseFloat(p.pnl).toFixed(2)} USDT</td>
<td class="text-end pe-3"><button class="btn btn-sm btn-outline-danger py-0" onclick="closePosition(${p.id})">平仓</button></td>
</tr>
`;
});
}
document.querySelector('#position-table tbody').innerHTML = html;
} catch(e) {}
}
async function closePosition(id) {
if (!confirm('确定要平掉该仓位吗?')) return;
const res = await fetch('api.php?action=close_position', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id })
});
const json = await res.json();
alert(json.message);
updatePositions();
}
async function submitOrder(side) {
const amount = document.getElementById(side.toLowerCase() + '-amount').value;
const leverageSelect = document.getElementById('leverage');
const leverage = leverageSelect ? leverageSelect.value : 1;
if (!amount || amount <= 0) { alert('请输入有效数量'); return; }
const res = await fetch('api.php?action=submit_order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ symbol, side, trade_type: tradeType, amount, leverage })
});
const json = await res.json();
if (json.status === 'success') {
alert('下单成功');
location.reload();
} else {
alert('错误: ' + json.message);
}
}
setInterval(updateMarket, 2000);
setInterval(updatePositions, 3000);
updateMarket();
updatePositions();
</script>
<?php include 'footer.php'; ?>

57
update_db.php Normal file
View File

@ -0,0 +1,57 @@
<?php
/**
* 数据库结构同步工具
* 访问此文件可确保您的宝塔数据库结构与当前代码一致
*/
require_once 'config.php';
try {
$db = db();
// 1. 确保 site_settings 表存在并有数据
$db->exec("CREATE TABLE IF NOT EXISTS site_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
site_name VARCHAR(100) DEFAULT 'BitCrypto',
contact_email VARCHAR(100) DEFAULT 'support@example.com',
deposit_address VARCHAR(255) DEFAULT 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
customer_service_url TEXT,
terms_content TEXT,
privacy_content TEXT,
is_pinning_active BOOLEAN DEFAULT FALSE
)");
$check = $db->query("SELECT count(*) FROM site_settings")->fetchColumn();
if ($check == 0) {
$db->exec("INSERT INTO site_settings (site_name) VALUES ('BitCrypto')");
}
// 2. 确保 accounts 表有 KYC 相关字段
$columns = $db->query("SHOW COLUMNS FROM accounts")->fetchAll(PDO::FETCH_COLUMN);
if (!in_array('kyc_status', $columns)) {
$db->exec("ALTER TABLE accounts ADD COLUMN kyc_status ENUM('UNVERIFIED', 'PENDING', 'VERIFIED', 'REJECTED') DEFAULT 'UNVERIFIED'");
}
if (!in_array('credit_score', $columns)) {
$db->exec("ALTER TABLE accounts ADD COLUMN credit_score INT DEFAULT 80");
}
// 3. 确保 cryptocurrencies 表存在
$db->exec("CREATE TABLE IF NOT EXISTS cryptocurrencies (
id INT AUTO_INCREMENT PRIMARY KEY,
symbol VARCHAR(20) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
icon_url TEXT,
current_price DECIMAL(30, 8) DEFAULT 0,
manual_price DECIMAL(30, 8) DEFAULT 0,
change_24h DECIMAL(10, 2) DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE
)");
echo "<h3>数据库同步完成!</h3>";
echo "<p>您的数据库结构已成功更新为最新版本。</p>";
echo "<p style='color:red;'><b>请立即删除 update_db.php 文件。</b></p>";
echo "<a href='index.php'>返回首页</a>";
} catch (Exception $e) {
echo "<h3>同步失败:</h3>";
echo "<pre>" . $e->getMessage() . "</pre>";
}

106
verify.php Normal file
View File

@ -0,0 +1,106 @@
<?php
include_once 'config.php';
check_auth();
$user_id = $_SESSION['user_id'];
$account = get_account($user_id);
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$real_name = $_POST['real_name'] ?? '';
$id_number = $_POST['id_number'] ?? '';
// In a real app, we would handle file uploads here.
// For this prototype, we'll just save the paths as placeholders.
$id_front = 'uploads/kyc/' . $user_id . '_front.jpg';
$id_back = 'uploads/kyc/' . $user_id . '_back.jpg';
if (empty($real_name) || empty($id_number)) {
$error = '请填写完整信息';
} else {
$stmt = db()->prepare("UPDATE accounts SET real_name = ?, id_number = ?, kyc_status = 'PENDING' WHERE id = ?");
$stmt->execute([$real_name, $id_number, $account['id']]);
$success = '认证资料已提交,请等待审核';
$account['kyc_status'] = 'PENDING';
}
}
include 'header.php';
?>
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="glass-card p-4">
<h3 class="text-white mb-4"><i class="bi bi-shield-check text-warning me-2"></i> 实名认证 (KYC)</h3>
<?php if ($success): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<div class="text-center py-5">
<i class="bi bi-clock-history display-1 text-warning"></i>
<p class="mt-3 text-secondary">审核中,通常在 24 小时内完成</p>
<a href="profile.php" class="btn btn-warning mt-3">返回个人中心</a>
</div>
<?php elseif ($account['kyc_status'] === 'VERIFIED'): ?>
<div class="text-center py-5">
<i class="bi bi-check-circle-fill display-1 text-success"></i>
<h4 class="mt-4">您已完成实名认证</h4>
<p class="text-secondary">感谢您的信任,您现在可以享受完整交易权限</p>
<a href="profile.php" class="btn btn-warning mt-3">返回个人中心</a>
</div>
<?php elseif ($account['kyc_status'] === 'PENDING'): ?>
<div class="text-center py-5">
<i class="bi bi-hourglass-split display-1 text-warning"></i>
<h4 class="mt-4">认证审核中</h4>
<p class="text-secondary">我们正在快马加鞭为您处理,请耐心等待</p>
<a href="profile.php" class="btn btn-warning mt-3">返回个人中心</a>
</div>
<?php else: ?>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<form method="POST">
<div class="mb-3">
<label class="form-label text-secondary small">真实姓名</label>
<input type="text" name="real_name" class="form-control bg-dark border-secondary text-white" placeholder="请输入您的真实姓名" required>
</div>
<div class="mb-3">
<label class="form-label text-secondary small">身份证号 / 护照号</label>
<input type="text" name="id_number" class="form-control bg-dark border-secondary text-white" placeholder="请输入证件号码" required>
</div>
<div class="row mb-4">
<div class="col-6">
<label class="form-label text-secondary small">证件正面</label>
<div class="border border-secondary border-dashed rounded p-4 text-center" style="border-style: dashed !important; cursor: pointer;">
<i class="bi bi-plus-lg text-secondary"></i>
<div class="small text-secondary mt-1">上传正面</div>
</div>
</div>
<div class="col-6">
<label class="form-label text-secondary small">证件反面</label>
<div class="border border-secondary border-dashed rounded p-4 text-center" style="border-style: dashed !important; cursor: pointer;">
<i class="bi bi-plus-lg text-secondary"></i>
<div class="small text-secondary mt-1">上传反面</div>
</div>
</div>
</div>
<div class="p-3 mb-4 rounded" style="background: rgba(252, 213, 53, 0.1); border: 1px solid rgba(252, 213, 53, 0.2);">
<div class="d-flex">
<i class="bi bi-info-circle text-warning me-2"></i>
<div class="small text-secondary">
请确保上传的图片清晰可见,文字无遮挡。您的个人信息将受到最高级别的安全保护。
</div>
</div>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-3">提交认证</button>
</form>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php include 'footer.php'; ?>

70
withdraw.php Normal file
View File

@ -0,0 +1,70 @@
<?php
include_once 'config.php';
check_auth();
$account = get_account($_SESSION['user_id']);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$amount = (float)($_POST['amount'] ?? 0);
$address = $_POST['address'] ?? '';
if ($amount >= 10 && $address) {
if ($account['balance'] >= $amount) {
try {
$db = db();
$db->beginTransaction();
// Deduct balance and freeze it
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ?, frozen_balance = frozen_balance + ? WHERE id = ?");
$stmt->execute([$amount, $amount, $account['id']]);
// Record transaction
$stmt = $db->prepare("INSERT INTO transactions (account_id, transaction_type, amount, tx_hash, status) VALUES (?, 'withdraw', ?, ?, 'pending')");
$stmt->execute([$account['id'], $amount, $address]);
$db->commit();
$success = "提现申请已提交,资金已冻结,请等待审核。";
$account = get_account($_SESSION['user_id']); // refresh
} catch (Exception $e) {
$db->rollBack();
$error = "系统错误: " . $e->getMessage();
}
} else {
$error = "余额不足。";
}
} else {
$error = "请输入有效金额(最小10)和地址。";
}
}
include 'header.php';
?>
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="glass-card p-4 bg-dark">
<h4 class="text-white mb-4"><i class="bi bi-box-arrow-up text-warning me-2"></i> 提现 USDT</h4>
<?php if(isset($success)): ?><div class="alert alert-success"><?php echo $success; ?></div><?php endif; ?>
<?php if(isset($error)): ?><div class="alert alert-danger"><?php echo $error; ?></div><?php endif; ?>
<div class="mb-4 d-flex justify-content-between">
<span class="text-secondary">可用余额:</span>
<span class="text-white fw-bold"><?php echo number_format($account['balance'], 2); ?> USDT</span>
</div>
<form method="POST">
<div class="mb-3">
<label class="form-label text-secondary">提现金额</label>
<input type="number" name="amount" step="0.01" class="form-control bg-dark text-white border-secondary" required>
</div>
<div class="mb-3">
<label class="form-label text-secondary">收币地址 (TRC20)</label>
<input type="text" name="address" class="form-control bg-dark text-white border-secondary" placeholder="T..." required>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 mt-3">申请提现</button>
</form>
</div>
</div>
</div>
</div>
<?php include 'footer.php'; ?>