修复PHP三个关键bug:1.player.php效果字段名clear_all_bad_effects→clear_all_bad匹配DB列名;2.validation_callback的add_visitor写入web_area_permissions表(与handleAddVisitor一致,修复成员不入库);3.validatePlayerViaJava优先本地playerExists验证,减少异步等待;4.auth兜底保底防止update_land_field误入invalid secret

This commit is contained in:
ypsdf1
2026-07-10 22:00:31 +08:00
parent b63ecc02b8
commit 555b9382b5
2 changed files with 56 additions and 12 deletions
+54 -10
View File
@@ -147,7 +147,23 @@ try {
$playerName = is_array($tokenInfo) ? $tokenInfo['player'] : $tokenInfo; $playerName = is_array($tokenInfo) ? $tokenInfo['player'] : $tokenInfo;
} else { } else {
// 未知action:默认放行(部分公开GET读取) // 未知action:默认放行(部分公开GET读取)
if ($method === 'POST' && !validateSecret($secret)) { // ★ 防御性保底:如果前面的数组检查遗漏了已知的玩家端action,在这里兜底
$knownPlayerActions = array_merge(
$playerActions ?? [], $playerPermActions ?? [], $memberPermActions ?? [], $playerFieldActions ?? []
);
if (in_array($action, $knownPlayerActions)) {
$token = $_GET['token'] ?? $_POST['token'] ?? '';
if (empty($token)) {
echo json_encode(['success' => false, 'error' => '需要登录', 'needLogin' => true]);
exit;
}
$tokenInfo = validateTokenSilent($token);
if (!$tokenInfo) {
echo json_encode(['success' => false, 'error' => 'token无效或已过期', 'needLogin' => true]);
exit;
}
$playerName = is_array($tokenInfo) ? $tokenInfo['player'] : $tokenInfo;
} elseif ($method === 'POST' && !validateSecret($secret)) {
echo json_encode(['success' => false, 'error' => 'invalid secret']); echo json_encode(['success' => false, 'error' => 'invalid secret']);
exit; exit;
} }
@@ -1159,7 +1175,23 @@ function validatePlayerViaJava($db, $playerName, $requestType = 'general', $extr
// status='pending' → 还在验证中 // status='pending' → 还在验证中
} }
// 2. 无缓存或已过期 → 写入pending表,触发Java异步验证 // 2. 直接查本地DB验证(不依赖Java异步验证)
if (playerExists($db, $playerName)) {
// 写入缓存,避免下次再走异步
try {
$stmtCache = $db->prepare("INSERT INTO pending_player_validations (player_name, request_type, request_data, status, created_at, validated_at)
VALUES (:player, :type, :data, 'valid', :now, :now)");
$stmtCache->bindValue(':player', $playerName, SQLITE3_TEXT);
$stmtCache->bindValue(':type', $requestType, SQLITE3_TEXT);
$stmtCache->bindValue(':data', json_encode($extraData), SQLITE3_TEXT);
$stmtCache->bindValue(':now', time(), SQLITE3_INTEGER);
$stmtCache->execute();
} catch (\Throwable $e) { /* 静默 */ }
debugLog("validatePlayerViaJava: 本地验证通过", ['player' => $playerName]);
return true;
}
// 3. 本地找不到 → 写入pending表,触发Java异步验证
$stmt2 = $db->prepare("INSERT INTO pending_player_validations (player_name, request_type, request_data, status, created_at) $stmt2 = $db->prepare("INSERT INTO pending_player_validations (player_name, request_type, request_data, status, created_at)
VALUES (:player, :type, :data, 'pending', :now)"); VALUES (:player, :type, :data, 'pending', :now)");
$stmt2->bindValue(':player', $playerName, SQLITE3_TEXT); $stmt2->bindValue(':player', $playerName, SQLITE3_TEXT);
@@ -1318,17 +1350,29 @@ function handleValidationCallback($db, $data) {
debugLog("validation_callback: 自动执行add_group_member", ['player' => $player, 'group' => $group]); debugLog("validation_callback: 自动执行add_group_member", ['player' => $player, 'group' => $group]);
} }
// --- add_visitor: 自动将玩家加入领地访客列表 --- // --- add_visitor: 自动将玩家加入领地访客列表(写入web_area_permissions,与handleAddVisitor一致)---
if ($reqType === 'add_visitor' && !empty($reqData['land'])) { if ($reqType === 'add_visitor' && !empty($reqData['land'])) {
$landName = $reqData['land']; $landName = $reqData['land'];
$now = time(); $now = time();
try { try {
$stmt5 = $db->prepare("INSERT OR IGNORE INTO web_area_visitors (land_name, player_name, added_by, added_time) // 先查land_id
VALUES (:land, :player, 'admin', :time)"); $stmtLand = $db->prepare("SELECT id FROM web_area_lands WHERE name = :name");
$stmt5->bindValue(':land', $landName, SQLITE3_TEXT); $stmtLand->bindValue(':name', $landName, SQLITE3_TEXT);
$stmt5->bindValue(':player', $player, SQLITE3_TEXT); $rsLand = $stmtLand->execute();
$stmt5->bindValue(':time', $now, SQLITE3_INTEGER); $landRow = $rsLand->fetchArray(SQLITE3_ASSOC);
$stmt5->execute(); $landId = $landRow ? (int)$landRow['id'] : 0;
if ($landId > 0) {
$stmt5 = $db->prepare("INSERT OR REPLACE INTO web_area_permissions
(land_id, land_name, player_name, role, permissions, granted_at, expires_at, synced_at)
VALUES (:land_id, :land_name, :player, 'visitor', '', :now, 0, :now)");
$stmt5->bindValue(':land_id', $landId, SQLITE3_INTEGER);
$stmt5->bindValue(':land_name', $landName, SQLITE3_TEXT);
$stmt5->bindValue(':player', $player, SQLITE3_TEXT);
$stmt5->bindValue(':time', $now, SQLITE3_INTEGER);
$stmt5->bindValue(':now', $now, SQLITE3_INTEGER);
$stmt5->execute();
}
// 写入变更队列,通知Java端 // 写入变更队列,通知Java端
$stmt6 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at) $stmt6 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at)
@@ -1338,7 +1382,7 @@ function handleValidationCallback($db, $data) {
$stmt6->bindValue(':data', json_encode(['action' => 'add_visitor', 'player' => $player]), SQLITE3_TEXT); $stmt6->bindValue(':data', json_encode(['action' => 'add_visitor', 'player' => $player]), SQLITE3_TEXT);
$stmt6->bindValue(':time', $now, SQLITE3_INTEGER); $stmt6->bindValue(':time', $now, SQLITE3_INTEGER);
$stmt6->execute(); $stmt6->execute();
} catch (\Throwable $e) { /* 静默 */ } } catch (\Throwable $e) { debugLog("validation_callback add_visitor error: " . $e->getMessage()); }
debugLog("validation_callback: 自动执行add_visitor", ['player' => $player, 'land' => $landName]); debugLog("validation_callback: 自动执行add_visitor", ['player' => $player, 'land' => $landName]);
} }
+2 -2
View File
@@ -2873,13 +2873,13 @@ async function renderLandDetail(el, landName) {
// ★★★ 效果管理 ★★★ // ★★★ 效果管理 ★★★
const clearEffects = land.clear_effects ? JSON.parse(land.clear_effects) : []; const clearEffects = land.clear_effects ? JSON.parse(land.clear_effects) : [];
const giveEffects = land.give_effects ? JSON.parse(land.give_effects) : []; const giveEffects = land.give_effects ? JSON.parse(land.give_effects) : [];
const clearAllBad = !!parseInt(land.clear_all_bad_effects || 0); const clearAllBad = !!parseInt(land.clear_all_bad || 0);
const denyAll = !!parseInt(land.deny_all_effects || 0); const denyAll = !!parseInt(land.deny_all_effects || 0);
html += `<div class="card" style="margin-top:12px"> html += `<div class="card" style="margin-top:12px">
<h3 style="margin:0 0 12px;color:var(--fg)">✨ 效果管理</h3> <h3 style="margin:0 0 12px;color:var(--fg)">✨ 效果管理</h3>
<div style="display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap"> <div style="display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap">
<span onclick="toggleLandField('${escHtml(land.name)}','clear_all_bad_effects')" <span onclick="toggleLandField('${escHtml(land.name)}','clear_all_bad')"
style="padding:6px 14px;border-radius:20px;font-size:12px;cursor:pointer;transition:all 0.2s; style="padding:6px 14px;border-radius:20px;font-size:12px;cursor:pointer;transition:all 0.2s;
background:${clearAllBad ? 'var(--green)' : 'var(--bg)'};color:${clearAllBad ? '#fff' : 'var(--dim)'}; background:${clearAllBad ? 'var(--green)' : 'var(--bg)'};color:${clearAllBad ? '#fff' : 'var(--dim)'};
border:1px solid ${clearAllBad ? 'var(--green)' : 'var(--border)'}"> border:1px solid ${clearAllBad ? 'var(--green)' : 'var(--border)'}">