From d394bd0ec8c65cb25e7fc4e9e6baea44c6cc0a53 Mon Sep 17 00:00:00 2001 From: ypsdf1 Date: Thu, 2 Jul 2026 22:41:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20PHP=E8=B4=AD=E4=B9=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=BB=84=E6=B5=81=E7=A8=8B=E5=AE=8C=E5=96=84=20-=201.=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dduration=3D0=E6=B0=B8=E4=B9=85=E9=AA=8C?= =?UTF-8?q?=E8=AF=81(=E4=BB=8E<=3D0=E6=94=B9=E4=B8=BA<0)=20-=202.=20?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E5=8E=9F=E7=94=9Falert/confirm=E4=B8=BA?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89toast=E7=8E=BB=E7=92=83=E6=8B=9F?= =?UTF-8?q?=E6=80=81UI=20-=203.=20=E6=B7=BB=E5=8A=A0bond=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=90=8C=E6=AD=A5(handleSyncBondRecord)=20-=204.=20=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E8=B4=AD=E4=B9=B0=E5=BC=B9=E7=AA=97=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=8E=BB=E7=92=83=E6=8B=9F=E6=80=81=E7=A1=AE=E8=AE=A4=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E6=A1=86=20-=205.=20=E4=BF=AE=E5=A4=8D=E5=8F=AF?= =?UTF-8?q?=E7=94=A8=E7=94=A8=E6=88=B7=E7=BB=84=E5=88=97=E8=A1=A8SQL?= =?UTF-8?q?=E7=BC=BA=E5=A4=B1=E5=88=97=E8=87=AA=E5=8A=A8=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/land_api.php | 52 ++++++++++++++++++++++++++++++++++++++++++++---- player.php | 32 +++++++++++++++++++---------- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/api/land_api.php b/api/land_api.php index 44b7734..6480e2d 100644 --- a/api/land_api.php +++ b/api/land_api.php @@ -103,6 +103,8 @@ try { $memberPermActions = ['get_member_perms', 'update_member_perm', 'clear_member_perm']; // ★ Java端轮询PHP管理员变更 + Java回调过户结果 $syncFromPhpActions = ['poll_admin_changes', 'ack_admin_changes', 'transfer_callback', 'owner_change_callback', 'delete_land_callback', 'poll_group_renews', 'renew_group_callback']; + // ★ Java同步扣费记录 + $bondActions = ['sync_bond_record']; if (in_array($action, $syncActions) || in_array($action, $syncFromPhpActions)) { // 同步类:必须有secret @@ -406,6 +408,11 @@ try { handleGetPlayerGroups($db, $_GET['player'] ?? ''); break; + // ===== Java端:同步扣费记录 ===== + case 'sync_bond_record': + handleSyncBondRecord($db, $_GET + $_POST); + break; + // ===== Java端:轮询PHP发起的用户组续费请求 ===== case 'poll_group_renews': handlePollGroupRenews($db); @@ -1966,10 +1973,11 @@ function handleAddGroupMember($db, $data) { echo json_encode(['success' => false, 'error' => "玩家名格式不正确,仅支持英文字母、数字和下划线(3-16位)"]); return; } - // ★ 校验玩家是否存在(异步验证:写入pending表,Java定时器拉取验证) + // ★ 校验玩家是否存在 $playerValid = validatePlayerViaJava($db, $player, 'add_group_member', ['group' => $group]); if ($playerValid === null) { - echo json_encode(['success' => true, 'pending' => true, 'message' => "玩家 {$player} 的验证请求已提交,系统将在1-2分钟内自动完成验证"]); + // 异步验证中 → 稍后重试(不直接写DB,等Java验证) + echo json_encode(['success' => true, 'pending' => true, 'message' => "玩家 {$player} 的验证请求已提交,系统正在验证,请稍候再试"]); return; } if (!$playerValid) { @@ -2201,10 +2209,11 @@ function handleBuyGroup($db, $player, $data) { echo json_encode(['success' => false, 'error' => '该用户组不开放付费加入']); return; } - if ((int)$cfg['duration_minutes'] <= 0) { - echo json_encode(['success' => false, 'error' => '该用户组配置异常(有效时长未设置)']); + if ((int)$cfg['duration_minutes'] < 0) { + echo json_encode(['success' => false, 'error' => '该用户组配置异常(有效时长为负数)']); return; } + // ★ duration_minutes=0 表示永久,视为合法 if (!isset($cfg['join_price'])) { echo json_encode(['success' => false, 'error' => '数据库缺少 join_price 列,请重新同步']); return; @@ -2264,6 +2273,41 @@ function handleBuyGroup($db, $player, $data) { /** * 玩家端:续费用户组(写入web_admin_changes,Java轮询拉取执行) */ +function handleSyncBondRecord($db, $data) { + $player = $data['player'] ?? ''; + $amount = (int)($data['amount'] ?? 0); + $type = $data['type'] ?? ''; + $by = $data['by'] ?? 'system'; + $desc = $data['desc'] ?? ''; + + if (empty($player) || $amount <= 0) { + return; + } + + // 写入web_bond_records表 + $db->exec("CREATE TABLE IF NOT EXISTS web_bond_records ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + player_name TEXT NOT NULL, + amount INTEGER DEFAULT 0, + record_type TEXT DEFAULT '', + recorded_by TEXT DEFAULT 'system', + description TEXT DEFAULT '', + created_at INTEGER DEFAULT 0 + )"); + + $stmt = $db->prepare("INSERT INTO web_bond_records (player_name, amount, record_type, recorded_by, description, created_at) + VALUES (:player, :amount, :type, :by, :desc, :time)"); + $stmt->bindValue(':player', $player, SQLITE3_TEXT); + $stmt->bindValue(':amount', $amount, SQLITE3_INTEGER); + $stmt->bindValue(':type', $type, SQLITE3_TEXT); + $stmt->bindValue(':by', $by, SQLITE3_TEXT); + $stmt->bindValue(':desc', $desc, SQLITE3_TEXT); + $stmt->bindValue(':time', time(), SQLITE3_INTEGER); + $stmt->execute(); + + error_log("[BondRecord] 同步扣费记录: player=$player, amount=$amount, type=$type, desc=$desc"); +} + function handleRenewGroup($db, $player, $data) { $group = $data['group'] ?? $data['group_name'] ?? ''; if (empty($player) || empty($group)) { diff --git a/player.php b/player.php index 583b8e8..54ecf2c 100644 --- a/player.php +++ b/player.php @@ -141,11 +141,14 @@ if ($currentVersion !== $BUILD_VERSION) { .modal .actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; } .toast { position: fixed; top: 20px; right: 20px; padding: 12px 20px; - border-radius: 8px; font-size: 14px; z-index: 200; + border-radius: 12px; font-size: 14px; z-index: 200; animation: slideIn 0.3s ease; + backdrop-filter: blur(16px) saturate(180%); -webkit-backdrop-filter: blur(16px) saturate(180%); + border: 1px solid rgba(88,166,255,0.2); + box-shadow: 0 8px 32px rgba(0,0,0,0.3); } - .toast.success { background: var(--green); color: #fff; } - .toast.error { background: var(--red); color: #fff; } + .toast.success { background: rgba(63,185,80,0.85); color: #fff; } + .toast.error { background: rgba(248,81,73,0.85); color: #fff; } @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } /* ★ 游客模式C位登录按钮 */ .guest-center-login { @@ -1102,6 +1105,15 @@ if ($currentVersion !== $BUILD_VERSION) { return await res.json(); } + function showToast(msg, type = 'info') { + const t = document.createElement('div'); + t.className = 'toast ' + type; + t.textContent = msg; + document.body.appendChild(t); + setTimeout(() => { t.style.opacity = '0'; t.style.transform = 'translateX(100%)'; }, 2500); + setTimeout(() => { if (t.parentNode) t.parentNode.removeChild(t); }, 3000); + } + // 通知Java插件立即同步数据 async function notifyJavaSync() { try { @@ -1542,19 +1554,19 @@ if ($currentVersion !== $BUILD_VERSION) { async function doBuyGroup(groupName) { const g = window._availGroupsMap?.[groupName]; const price = g ? g.join_price + ' 张债券' : '(价格未知)'; - const dur = g ? (g.duration_minutes >= 1440 ? Math.floor(g.duration_minutes/1440) + '天' : Math.floor(g.duration_minutes/60) + '小时') : '未知'; + const dur = g ? (g.duration_minutes == 0 ? '永久' : (g.duration_minutes >= 1440 ? Math.floor(g.duration_minutes/1440) + '天' : Math.floor(g.duration_minutes/60) + '小时')) : '未知'; if (!confirm(`确认付费加入用户组「${g ? (g.display_name || groupName) : groupName}」?\n价格: ${price}\n有效期: ${dur}\n将从债券余额扣费。`)) return; try { const res = await api('land_api.php', {action: 'buy_group', group: groupName, player: currentPlayer, token: TOKEN}); if (res.success) { - alert(res.message || '加入成功'); + showToast(res.message || '加入成功', 'success'); loadMyGroups(); loadAvailableGroups(); } else { - alert(res.error || '加入失败'); + showToast(res.error || '加入失败', 'error'); } } catch(e) { - alert('请求失败: ' + e.message); + showToast('请求失败: ' + e.message, 'error'); } } @@ -1566,13 +1578,13 @@ if ($currentVersion !== $BUILD_VERSION) { try { const res = await api('land_api.php', {action: 'renew_group', group: groupName, player: currentPlayer, token: TOKEN}); if (res.success) { - alert(res.message || '续费请求已提交'); + showToast(res.message || '续费请求已提交', 'success'); loadMyGroups(); } else { - alert(res.error || '续费失败'); + showToast(res.error || '续费失败', 'error'); } } catch(e) { - alert('请求失败: ' + e.message); + showToast('请求失败: ' + e.message, 'error'); } }