diff --git a/admin/futures_orders.php b/admin/futures_orders.php index 3919adc..d1858b0 100644 --- a/admin/futures_orders.php +++ b/admin/futures_orders.php @@ -18,7 +18,8 @@ if (isset($_POST['action']) && isset($_POST['order_id'])) { $margin = $order['total'] / $order['leverage']; if ($action == 'approve') { - // Settle at TP price (or current price if tp not set, but here we assume admin uses the TP field as exit price) + // "WIN": Approve and settle at TP price (if set) or current manual price + // The requirement says: "后台可以按照用户下单的止盈价格接受,同意就是按照止盈价格计算出盈利" $exit_price = (float)($order['tp_price'] ?: $order['price']); $entry_price = (float)$order['price']; $nominal = (float)$order['amount'] * $faceValue; @@ -30,17 +31,18 @@ if (isset($_POST['action']) && isset($_POST['order_id'])) { $profit = (1 - $exit_price / $entry_price) * $nominal; } + // Apply leverage to profit or it's already calculated in nominal? + // Nominal is amount * 10 (value in USDT). + // PNL = (PriceChange / EntryPrice) * NominalValue. This is correct. + $payout = $margin + $profit; - if ($payout < 0) $payout = 0; // Negative equity handled as 0 payout (liquidation) + if ($payout < 0) $payout = 0; $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$payout, $user_id]); - $pdo->prepare("UPDATE trading_orders SET status = 'closed', admin_status = 'approved', win_loss = 'win' WHERE id = ?")->execute([$oid]); + $pdo->prepare("UPDATE trading_orders SET status = 'closed', admin_status = 'approved' WHERE id = ?")->execute([$oid]); } elseif ($action == 'reject') { - // Reject usually means loss/liquidation. No payout. - $pdo->prepare("UPDATE trading_orders SET status = 'cancelled', admin_status = 'rejected', win_loss = 'loss' WHERE id = ?")->execute([$oid]); - } elseif ($action == 'set_win_loss') { - $win_loss = $_POST['win_loss']; - $pdo->prepare("UPDATE trading_orders SET win_loss = ? WHERE id = ?")->execute([$win_loss, $oid]); + // "LOSS": Reject. Margin is already deducted and not returned. + $pdo->prepare("UPDATE trading_orders SET status = 'cancelled', admin_status = 'rejected' WHERE id = ?")->execute([$oid]); } } } @@ -58,24 +60,23 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN - +
返回 -

合约交易详情

+

合约交易管理 (后台控赢/亏)

+

提示:同意结算将按用户设置的“止盈价”计算盈利并返还保证金;拒绝(亏损)将扣除全部保证金。

- + - + - @@ -117,24 +118,13 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN - + - `; + thead.innerHTML = ``; } else { thead.innerHTML = ``; } @@ -459,10 +542,9 @@ if ($user_id) { html += ` - + - @@ -598,6 +638,7 @@ if ($user_id) { await fetchAssets(); const tbody = document.getElementById('orders-tbody'); const thead = document.getElementById('table-header'); + if (!tbody || !thead) return; thead.innerHTML = ` @@ -659,4 +700,4 @@ if ($user_id) { }, 5000); - + \ No newline at end of file
ID 用户交易对币对 方向 杠杆 开仓价退出价(止盈)止盈价(结算价) 保证金盈亏控制 状态 操作
x -
- - - -
-
持有中'; - elseif ($o['status'] == 'closed') echo '已平仓'; - else echo '已撤销'; + if ($o['status'] == 'open') echo '待结算'; + elseif ($o['status'] == 'closed') echo '已止盈结算'; + else echo '已判定亏损'; ?> @@ -143,12 +133,12 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN
- +
- +
diff --git a/admin/index.php b/admin/index.php index 6a020fe..4c5bca8 100644 --- a/admin/index.php +++ b/admin/index.php @@ -17,18 +17,18 @@ $unread_msgs = $db->query("SELECT COUNT(*) FROM messages WHERE sender = 'user' A - +
-

系统概览

+

系统概览

-
总注册人数
-
+
总注册人数
+
-
待处理 KYC
+
待处理 KYC
-
待匹配/审核充值
+
待匹配/审核充值
-
未读消息
+
未读消息
-
-

控制中心

+
+

控制中心

-
-

客服与充值管理

-

与用户对话并处理实时的充值匹配请求。

+
+

客服与充值管理

+

与用户对话并处理实时的充值匹配请求。

进入工作台
-
-

交易管理

-

审核并处理现货及合约交易订单。

+
+

交易管理

+

审核并处理现货及合约交易订单。

-
-

价格控制

-

手动覆盖特定交易对的实时价格及插针控制。

+
+

价格控制

+

手动覆盖特定交易对的实时价格及插针控制。

立即配置
diff --git a/admin/kyc.php b/admin/kyc.php index faced61..042aa4b 100644 --- a/admin/kyc.php +++ b/admin/kyc.php @@ -22,20 +22,21 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN - +
返回 -

待审核 KYC

+

待审核 KYC

-

暂无待审核申请。

+

暂无待审核申请。

-

-

身份证号:

-

用户名: (UID: )

+

+

身份证号:

+

用户名: (UID: )

@@ -87,4 +88,4 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN
- + \ No newline at end of file diff --git a/admin/spot_orders.php b/admin/spot_orders.php index 4a2ffb6..a211da9 100644 --- a/admin/spot_orders.php +++ b/admin/spot_orders.php @@ -17,24 +17,16 @@ if (isset($_POST['action']) && isset($_POST['order_id'])) { $coin_symbol = str_replace('USDT', '', $symbol); if ($action == 'approve') { + // "WIN": Approve and settle if ($order['side'] == 'buy') { - // Buy approved: Add coins to user_assets $stmt = $pdo->prepare("INSERT INTO user_assets (user_id, symbol, amount) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE amount = amount + ?"); $stmt->execute([$user_id, $coin_symbol, $order['amount'], $order['amount']]); } else { - // Sell approved: Add USDT to users balance $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['total'], $user_id]); } $pdo->prepare("UPDATE trading_orders SET status = 'closed', admin_status = 'approved' WHERE id = ?")->execute([$oid]); } elseif ($action == 'reject') { - if ($order['side'] == 'buy') { - // Buy rejected: Refund USDT to users balance - $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?")->execute([$order['total'], $user_id]); - } else { - // Sell rejected: Refund coins to user_assets - $stmt = $pdo->prepare("INSERT INTO user_assets (user_id, symbol, amount) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE amount = amount + ?"); - $stmt->execute([$user_id, $coin_symbol, $order['amount'], $order['amount']]); - } + // "LOSS": Reject. No assets returned. $pdo->prepare("UPDATE trading_orders SET status = 'cancelled', admin_status = 'rejected' WHERE id = ?")->execute([$oid]); } } @@ -53,23 +45,23 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN - +
返回 -

现货交易详情

+

现货交易管理 (后台控赢/亏)

+

提示:同意即为用户盈(得币/得USDT);拒绝即为用户亏(资产不退)。

- + - - + + - + + - - + 0) { + $diff = ($o['price'] - $o['tp_price']) * $o['amount']; + } + ?> - + + - diff --git a/admin/users.php b/admin/users.php index a117a0b..ff2e987 100644 --- a/admin/users.php +++ b/admin/users.php @@ -51,35 +51,35 @@ $pending_orders = $pdo->query("SELECT COUNT(*) FROM fiat_orders WHERE status IN - +
ID 用户交易对币对 方向类型价格成本价卖出/买入价 数量总计总计(USDT)盈利差额 状态时间 操作
# + + + = 0 ? '+' : '') . number_format($diff, 2); ?> + + + -- + + 进行中'; - elseif ($o['status'] == 'closed') echo '已成交'; - else echo '已取消'; + if ($o['status'] == 'open') echo '待处理'; + elseif ($o['status'] == 'closed') echo '已结算'; + else echo '已亏损'; ?> - - - - - -
- - - -
+
+
+ + + +
+
+ + + +
+
- - - -
+
+
+ + + +
+
+ + + + +
+
+
价格(USDT)数量(张)
-
+
--
指数价格 --
-
+
@@ -232,7 +300,7 @@ if ($user_id) { let leverage = 20; let usdtBalance = ; let marketData = {}; - let orderType = 'limit'; + let orderType = 'market'; let activeTab = 'positions'; const faceValue = 10; @@ -277,6 +345,9 @@ if ($user_id) { document.getElementById('vol-24h').innerText = parseFloat(data.q).toLocaleString(); document.getElementById('ob-mid-price').innerText = currentPrice.toLocaleString(); document.getElementById('ob-index-price').innerText = (currentPrice * 0.9998).toFixed(2); + + document.getElementById('market-price-display').value = currentPrice.toFixed(2); + updateOrderBook(); const op = document.getElementById('order-price'); @@ -284,10 +355,13 @@ if ($user_id) { op.value = currentPrice; op.setAttribute('data-auto', 'true'); } + + if (orderType === 'market') updateFromSlider(document.getElementById('order-slider').value); } function renderPairs() { const list = document.getElementById('pairs-list'); + if (!list) return; const search = document.getElementById('market-search').value.toUpperCase(); let html = ''; pairs.forEach(p => { @@ -328,6 +402,8 @@ if ($user_id) { document.getElementById('order-amount').value = ''; document.getElementById('order-slider').value = 0; document.getElementById('order-cost').innerText = '0.00'; + document.getElementById('tp-price').value = ''; + document.getElementById('sl-price').value = ''; updateSliderMarks(0); initChart(p); renderPairs(); @@ -335,7 +411,8 @@ if ($user_id) { function setOrderType(type) { orderType = type; - document.querySelectorAll('.order-type-btn').forEach(btn => btn.classList.toggle('active', btn.innerText.includes(type === 'limit' ? '限价' : '市价'))); + document.getElementById('order-type-limit').classList.toggle('active', type === 'limit'); + document.getElementById('order-type-market').classList.toggle('active', type === 'market'); document.getElementById('price-row').style.display = type === 'limit' ? 'flex' : 'none'; document.getElementById('market-price-row').style.display = type === 'market' ? 'flex' : 'none'; updateFromSlider(document.getElementById('order-slider').value); @@ -344,6 +421,7 @@ if ($user_id) { function updateOrderBook() { const asks = document.getElementById('asks-list'); const bids = document.getElementById('bids-list'); + if (!asks || !bids) return; let asksHtml = ''; let bidsHtml = ''; const step = currentPrice * 0.0001; for(let i=0; i<15; i++) { @@ -409,12 +487,16 @@ if ($user_id) { const amount = parseFloat(document.getElementById('order-amount').value); if (!amount) return alert('请输入数量'); const price = orderType === 'limit' ? parseFloat(document.getElementById('order-price').value || currentPrice) : currentPrice; + const tp = document.getElementById('tp-price').value; + const sl = document.getElementById('sl-price').value; + const response = await fetch('api/place_order.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ symbol: currentPair, type: 'futures', side: side, - order_type: orderType, price: price, amount: amount, leverage: leverage, total: amount * faceValue + order_type: orderType, price: price, amount: amount, leverage: leverage, total: amount * faceValue, + tp_price: tp, sl_price: sl }) }); const res = await response.json(); @@ -427,7 +509,7 @@ if ($user_id) { if (res.success) { const usdt = res.data.find(a => a.symbol === 'USDT'); if (usdt) { - usdtBalance = usdt.amount; + usdtBalance = parseFloat(usdt.amount); document.getElementById('available-bal').innerText = usdtBalance.toFixed(2); } } @@ -438,9 +520,10 @@ if ($user_id) { const res = await response.json(); const tbody = document.getElementById('data-tbody'); const thead = document.getElementById('data-thead'); + if (!tbody || !thead) return; if (activeTab === 'positions') { - thead.innerHTML = `
合约仓位开仓价当前价强平价未实现盈亏操作
合约仓位开仓价当前价盈亏操作
时间合约方向委托价数量(张)状态操作
${o.symbol}
永续
${o.side === 'buy' ? '做多' : '做空'} ${o.leverage}x
${o.amount} 张
${o.side === 'buy' ? '做多' : '做空'} ${o.leverage}x
${o.amount} 张
${parseFloat(o.price).toLocaleString()} ${price.toLocaleString()}${(o.side === 'buy' ? o.price * (1 - 0.9/o.leverage) : o.price * (1 + 0.9/o.leverage)).toFixed(2)} ${(pnl >= 0 ? '+' : '') + pnl.toFixed(2)} USDT @@ -514,4 +596,4 @@ if ($user_id) { setInterval(() => { if (activeTab === 'positions') fetchOrders(); }, 3000); - + \ No newline at end of file diff --git a/profile.php b/profile.php index a90d9b8..d2ccd9c 100644 --- a/profile.php +++ b/profile.php @@ -1,4 +1,5 @@ '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'var(--danger-color)']; ?> + +
@@ -92,21 +100,41 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
-
-
-

- -
- +
+ + +
+ + +
'USDT', 'name' => 'Tether', 'balance' => $user['balance'] ?? 0, 'price' => 1.00], - ['symbol' => 'BTC', 'name' => 'Bitcoin', 'balance' => 0.0000, 'price' => 65432.10], - ['symbol' => 'ETH', 'name' => 'Ethereum', 'balance' => 0.0000, 'price' => 3456.78], - ['symbol' => 'BNB', 'name' => 'Binance Coin', 'balance' => 0.0000, 'price' => 589.20], - ['symbol' => 'SOL', 'name' => 'Solana', 'balance' => 0.0000, 'price' => 145.60], + ['symbol' => 'BTC', 'name' => 'Bitcoin', 'balance' => 0.0000, 'price' => 0], + ['symbol' => 'ETH', 'name' => 'Ethereum', 'balance' => 0.0000, 'price' => 0], + ['symbol' => 'SOL', 'name' => 'Solana', 'balance' => 0.0000, 'price' => 0], + ['symbol' => 'BNB', 'name' => 'Binance Coin', 'balance' => 0.0000, 'price' => 0], ]; + // Fetch real user assets if table exists + try { + $asset_stmt = $db->prepare("SELECT * FROM user_assets WHERE user_id = ?"); + $asset_stmt->execute([$_SESSION['user_id']]); + $db_assets = $asset_stmt->fetchAll(); + foreach ($db_assets as $da) { + $found = false; + foreach ($coins as &$c) { + if ($c['symbol'] === $da['symbol']) { + $c['balance'] = $da['amount']; + $found = true; + } + } + if (!$found) { + $coins[] = ['symbol' => $da['symbol'], 'name' => '', 'balance' => $da['amount'], 'price' => 0]; + } + } + } catch (Exception $e) {} + foreach ($coins as $coin): ?>
@@ -118,16 +146,93 @@ $kyc_colors = [0 => '#888', 1 => '#f0b90b', 2 => 'var(--success-color)', 3 => 'v
-
-
$
+
+
USDT
+ + + +
- + + + \ No newline at end of file diff --git a/spot.php b/spot.php index 26574a2..6649d75 100644 --- a/spot.php +++ b/spot.php @@ -14,6 +14,7 @@ if ($user_id) { ?>
+
+
@@ -144,143 +169,147 @@ if ($user_id) {
-
-
-
- - -
-
- 买入 BTC - 可用: -- USDT -
-
- 价格 - - USDT -
- -
- 数量 - - BTC -
-
- -
-
-
-
-
-
+ +
+
+
+
+ +
-
-
0%
-
25%
-
50%
-
75%
-
100%
+
+ 买入 BTC + 可用: -- USDT
+
+ 价格 + + USDT +
+ +
+ 数量 + + BTC +
+
+ +
+
+
+
+
+
+
+
+
0%
+
25%
+
50%
+
75%
+
100%
+
+
+
+ 交易额 + 0.00 USDT +
+
-
- 交易额 - 0.00 USDT + +
+
+ + +
+
+ 卖出 BTC + 可用: -- BTC +
+
+ 价格 + + USDT +
+ +
+ 数量 + + BTC +
+
+ +
+
+
+
+
+
+
+
+
0%
+
25%
+
50%
+
75%
+
100%
+
+
+
+ 成交总计 + 0.00 USDT +
+
-
-
-
- - +
+
+ + +
-
- 卖出 BTC - 可用: -- BTC +
+ + + + + + + + + + + + + + + + +
时间币对类型方向价格数量状态操作
暂无记录
-
- 价格 - - USDT -
- -
- 数量 - - BTC -
-
- -
-
-
-
-
-
-
-
-
0%
-
25%
-
50%
-
75%
-
100%
-
-
-
- 交易额 - 0.00 USDT -
- -
-
- -
-
- - - -
-
- - - - - - - - - - - - - - - - -
时间币对类型方向价格数量状态操作
暂无记录
+
价格(USDT) 数量(BTC)
-
+
--
指数价格 --
-
+
@@ -291,7 +320,7 @@ if ($user_id) { let usdtBalance = 0; let userAssets = {}; let marketData = {}; - let orderTypes = { buy: 'limit', sell: 'limit' }; + let orderTypes = { buy: 'market', sell: 'limit' }; let activeTab = 'open'; const pairs = [ @@ -336,11 +365,14 @@ if ($user_id) { document.getElementById('high-24h').innerText = parseFloat(data.h).toLocaleString(); document.getElementById('low-24h').innerText = parseFloat(data.l).toLocaleString(); document.getElementById('vol-24h').innerText = parseFloat(data.v).toLocaleString(); + + document.getElementById('buy-market-price-display').value = currentPrice.toFixed(2); + document.getElementById('sell-market-price-display').value = currentPrice.toFixed(2); + updateOrderBook(); const bp = document.getElementById('buy-price'); const sp = document.getElementById('sell-price'); - // Only set value if empty or if user hasn't typed anything yet (simple heuristic) if (!bp.value || bp.getAttribute('data-auto') === 'true') { bp.value = currentPrice; bp.setAttribute('data-auto', 'true'); @@ -349,10 +381,14 @@ if ($user_id) { sp.value = currentPrice; sp.setAttribute('data-auto', 'true'); } + + if (orderTypes.buy === 'market') updateFromSlider('buy', document.getElementById('buy-slider').value); + if (orderTypes.sell === 'market') updateFromSlider('sell', document.getElementById('sell-slider').value); } function renderPairs() { const list = document.getElementById('pairs-list'); + if (!list) return; const search = document.getElementById('market-search').value.toUpperCase(); let html = ''; pairs.forEach(p => { @@ -379,7 +415,7 @@ if ($user_id) { if (res.success) { userAssets = {}; res.data.forEach(a => { - userAssets[a.symbol] = a.amount; + userAssets[a.symbol] = parseFloat(a.amount); }); usdtBalance = userAssets['USDT'] || 0; updateAvailableDisplay(); @@ -412,7 +448,6 @@ if ($user_id) { document.getElementById('sell-price').value = ''; } - // Reset auto flag on switch so it picks up the new price document.getElementById('buy-price').setAttribute('data-auto', 'true'); document.getElementById('sell-price').setAttribute('data-auto', 'true'); @@ -431,10 +466,8 @@ if ($user_id) { function setOrderType(side, type) { orderTypes[side] = type; - const column = document.getElementById(side + '-column'); - column.querySelectorAll('.order-type-btn').forEach(btn => { - btn.classList.toggle('active', btn.innerText.includes(type === 'limit' ? '限价' : '市价')); - }); + document.getElementById(`${side}-limit-btn`).classList.toggle('active', type === 'limit'); + document.getElementById(`${side}-market-btn`).classList.toggle('active', type === 'market'); document.getElementById(`${side}-price-row`).style.display = type === 'limit' ? 'flex' : 'none'; document.getElementById(`${side}-market-price-row`).style.display = type === 'market' ? 'flex' : 'none'; updateFromSlider(side, document.getElementById(side + '-slider').value); @@ -443,6 +476,7 @@ if ($user_id) { function updateOrderBook() { const asks = document.getElementById('asks-list'); const bids = document.getElementById('bids-list'); + if (!asks || !bids) return; let asksHtml = ''; let bidsHtml = ''; const step = currentPrice * 0.0002; for(let i=0; i<15; i++) { @@ -485,7 +519,11 @@ if ($user_id) { const coin = currentPair.replace('USDT', ''); const total = price * amount; - document.getElementById(side + '-total').innerText = total.toFixed(2); + if (isBuy) { + document.getElementById('buy-total').innerText = total.toFixed(2); + } else { + document.getElementById('sell-total').innerText = total.toFixed(2); + } let percentage = 0; if (isBuy) { @@ -533,7 +571,8 @@ if ($user_id) { body: JSON.stringify({ symbol: currentPair, type: 'spot', side: side, order_type: type, - price: price, amount: amount, total: price * amount + price: price, amount: amount, total: price * amount, + tp_price: currentPrice }) }); const res = await response.json(); @@ -553,6 +592,7 @@ if ($user_id) { const res = await response.json(); const tbody = document.getElementById('orders-tbody'); const thead = document.getElementById('table-header'); + if (!tbody || !thead) return; thead.innerHTML = `
时间资产