R15: PHP添加成员改为Java验证后入库(参考改领地主流程)

- handleAddVisitor改为先写pending队列,等Java验证后入库
- 新增handleAddVisitorCallback回调PHP添加成员结果
- 新增handleGetAddVisitorStatus供前端轮询状态
- player.php添加成员后轮询状态,支持异步验证显示
This commit is contained in:
ypsdf1
2026-07-11 15:53:12 +08:00
parent 0115cbda41
commit d7da6ec1a6
2 changed files with 156 additions and 36 deletions
+121 -34
View File
@@ -92,7 +92,7 @@ try {
// 同步类action只接受secret验证(Java端推送) // 同步类action只接受secret验证(Java端推送)
$syncActions = ['sync_lands', 'sync_shop', 'sync_permissions', 'get_pending_validations', 'validation_callback']; $syncActions = ['sync_lands', 'sync_shop', 'sync_permissions', 'get_pending_validations', 'validation_callback'];
// 管理面板action:支持admin_token或secret // 管理面板action:支持admin_token或secret
$adminActions = ['list_lands', 'list_shop', 'get_config', 'update_config', 'delete_land', 'update_land_owner', 'delete_shop_item', 'list_user_groups', 'get_user_group', 'update_user_group', 'delete_user_group', 'list_group_members', 'add_group_member', 'remove_group_member']; $adminActions = ['list_lands', 'list_shop', 'get_config', 'update_config', 'delete_land', 'update_land_owner', 'delete_shop_item', 'list_user_groups', 'get_user_group', 'update_user_group', 'delete_user_group', 'list_group_members', 'add_group_member', 'remove_group_member', 'get_add_visitor_status'];
// 玩家端action:需要token // 玩家端action:需要token
$playerActions = ['my_lands', 'land_detail', 'add_visitor', 'remove_visitor', 'list_visitors', 'land_shop', 'buy_permission', 'transfer_land', 'cancel_transfer', 'transfer_status', 'renew_group', 'list_available_groups', 'buy_group', 'get_player_groups']; $playerActions = ['my_lands', 'land_detail', 'add_visitor', 'remove_visitor', 'list_visitors', 'land_shop', 'buy_permission', 'transfer_land', 'cancel_transfer', 'transfer_status', 'renew_group', 'list_available_groups', 'buy_group', 'get_player_groups'];
// ★ 玩家端领地字段更新(效果管理、开关等) // ★ 玩家端领地字段更新(效果管理、开关等)
@@ -102,7 +102,7 @@ try {
// ★ 成员独立权限操作action(领地所有者编辑成员权限) // ★ 成员独立权限操作action(领地所有者编辑成员权限)
$memberPermActions = ['get_member_perms', 'update_member_perm', 'clear_member_perm']; $memberPermActions = ['get_member_perms', 'update_member_perm', 'clear_member_perm'];
// ★ Java端轮询PHP管理员变更 + Java回调过户结果 // ★ Java端轮询PHP管理员变更 + Java回调过户结果
$syncFromPhpActions = ['poll_admin_changes', 'ack_admin_changes', 'transfer_callback', 'owner_change_callback', 'delete_land_callback', 'poll_group_renews', 'renew_group_callback']; $syncFromPhpActions = ['poll_admin_changes', 'ack_admin_changes', 'transfer_callback', 'owner_change_callback', 'add_visitor_callback', 'delete_land_callback', 'poll_group_renews', 'renew_group_callback'];
// ★ Java同步扣费记录 // ★ Java同步扣费记录
$bondActions = ['sync_bond_record']; $bondActions = ['sync_bond_record'];
// ★ Java端:推送成员变更到PHP(add/remove_group_member由secret调用) // ★ Java端:推送成员变更到PHP(add/remove_group_member由secret调用)
@@ -396,6 +396,11 @@ try {
handleOwnerChangeCallback($db, $_POST + $_GET); handleOwnerChangeCallback($db, $_POST + $_GET);
break; break;
// ===== Java端:管理面板添加成员结果回调 =====
case 'add_visitor_callback':
handleAddVisitorCallback($db, $_POST + $_GET);
break;
case 'delete_land_callback': case 'delete_land_callback':
handleDeleteLandCallback($db, $_GET); handleDeleteLandCallback($db, $_GET);
break; break;
@@ -453,6 +458,11 @@ try {
handleGetOwnerChangeStatus($db, $_GET); handleGetOwnerChangeStatus($db, $_GET);
break; break;
// ===== 管理面板:查询添加成员状态 =====
case 'get_add_visitor_status':
handleGetAddVisitorStatus($db, $_GET);
break;
default: default:
echo json_encode(['success' => false, 'error' => 'unknown action']); echo json_encode(['success' => false, 'error' => 'unknown action']);
} }
@@ -1142,39 +1152,17 @@ function handleAddVisitor($db, $playerName, $req) {
return; return;
} }
// ★ 直接插入访客(先执行操作,再异步验证) // ★ 参考改领地主流程:先写入pending队列,等Java验证玩家是否存在后再入库
$stmt2 = $db->prepare("INSERT OR REPLACE INTO web_area_permissions // 写入web_admin_changes让Java端pollAdminChanges拉取并验证执行
(land_id, land_name, player_name, role, permissions, granted_at, expires_at, synced_at) $stmt4 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at, status) VALUES ('add_visitor', :id, :name, :data, :now, 'pending')");
VALUES (:land_id, :land_name, :player, 'visitor', '', :now, 0, :now)"); $stmt4->bindValue(':id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER); $stmt4->bindValue(':name', $landName, SQLITE3_TEXT);
$stmt2->bindValue(':land_name', $landName, SQLITE3_TEXT); $stmt4->bindValue(':data', json_encode(['player' => $visitor, 'land_name' => $landName, 'role' => 'visitor', 'source' => 'admin_panel'], JSON_UNESCAPED_UNICODE), SQLITE3_TEXT);
$stmt2->bindValue(':player', $visitor, SQLITE3_TEXT); $stmt4->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt2->bindValue(':now', time(), SQLITE3_INTEGER); $stmt4->execute();
$stmt2->execute();
// ★ 写入变更队列通知Java同步(add_visitor) debugLog("handleAddVisitor: 管理面板添加成员 {$visitor} → {$landName} (已写入pending队列,等待Java验证)");
try { echo json_encode(['success' => true, 'pending' => true, 'message' => "添加成员请求已提交: [{$landName}] {$visitor},等待Java端验证后生效"]);
$changeStmt = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at) VALUES ('add_visitor', :lid, :name, :data, :now)");
$changeStmt->bindValue(':lid', (int)$land['id'], SQLITE3_INTEGER);
$changeStmt->bindValue(':name', $landName, SQLITE3_TEXT);
$changeStmt->bindValue(':data', json_encode(['player' => $visitor, 'land_name' => $landName, 'role' => 'visitor'], JSON_UNESCAPED_UNICODE), SQLITE3_TEXT);
$changeStmt->bindValue(':now', time(), SQLITE3_INTEGER);
$changeStmt->execute();
} catch (\Throwable $e) {
// 非致命
}
// ★ 异步验证玩家是否存在(仅记录日志,不阻塞操作)
try {
$visitorValid = validatePlayerViaJava($db, $visitor, 'add_visitor', ['land' => $landName]);
if ($visitorValid === false) {
error_log("[访客管理] 警告: 玩家 {$visitor} 可能不存在于login.db,但已添加为访客");
}
} catch (\Throwable $e) {
// 验证失败不阻塞
}
echo json_encode(['success' => true, 'message' => "已添加访客: $visitor"]);
} }
/** /**
@@ -3060,6 +3048,105 @@ function handleDeleteLandCallback($db, $get) {
echo json_encode(['success' => true, 'message' => "领地 [{$name}] 已删除"]); echo json_encode(['success' => true, 'message' => "领地 [{$name}] 已删除"]);
} }
/**
* ★ Java端管理面板添加成员结果回调
* 验证通过→更新PHP本地副本+记录变更;验证失败→标记失败
*/
function handleAddVisitorCallback($db, $post) {
$changeId = (int)($post['change_id'] ?? 0);
$success = (int)($post['success'] ?? 0);
$reason = $post['reason'] ?? '';
if ($changeId <= 0) {
echo json_encode(['success' => false, 'error' => '缺少change_id']);
return;
}
// 查找pending的add_visitor记录
$stmt = $db->prepare("SELECT * FROM web_admin_changes WHERE id = :id AND change_type = 'add_visitor' AND (status = 'pending' OR status IS NULL OR status = 'done')");
$stmt->bindValue(':id', $changeId, SQLITE3_INTEGER);
$rs = $stmt->execute();
$row = $rs->fetchArray(SQLITE3_ASSOC);
if (!$row) {
echo json_encode(['success' => false, 'error' => '变更记录不存在或已处理']);
return;
}
$targetName = $row['target_name'];
$changeData = json_decode($row['change_data'] ?? '{}', true);
$player = $changeData['player'] ?? '';
$role = $changeData['role'] ?? 'visitor';
$landName = $changeData['land_name'] ?? $targetName;
if ($success) {
// ★ Java验证通过:更新PHP本地副本
$stmt2 = $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, :role, '', :now, 0, :now)");
$stmt2->bindValue(':land_id', (int)($row['target_id'] ?? 0), SQLITE3_INTEGER);
$stmt2->bindValue(':land_name', $landName, SQLITE3_TEXT);
$stmt2->bindValue(':player', $player, SQLITE3_TEXT);
$stmt2->bindValue(':role', $role, SQLITE3_TEXT);
$stmt2->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt2->execute();
// 标记处理完成
$db->query("UPDATE web_admin_changes SET status = 'completed', acknowledged = 1, acked_at = " . time() . " WHERE id = " . $changeId);
debugLog("handleAddVisitorCallback: 添加成员 {$player} → {$landName} Java验证通过,已更新PHP副本");
echo json_encode(['success' => true, 'message' => '添加成员验证通过,已更新']);
} else {
// ★ Java验证失败:标记失败
$db->query("UPDATE web_admin_changes SET status = 'failed', acknowledged = 1, acked_at = " . time() . " WHERE id = " . $changeId);
debugLog("handleAddVisitorCallback: 添加成员 {$player} → {$landName} Java验证失败: {$reason}");
echo json_encode(['success' => true, 'message' => "添加成员验证失败: {$reason}"]);
}
}
/**
* ★ 管理面板:查询添加成员状态
*/
function handleGetAddVisitorStatus($db, $get) {
$name = $get['name'] ?? '';
if (empty($name)) {
echo json_encode(['success' => false, 'error' => '缺少name参数']);
return;
}
$stmt = $db->prepare("SELECT status, change_data FROM web_admin_changes WHERE change_type = 'add_visitor' AND target_name = :name ORDER BY id DESC LIMIT 1");
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
$rs = $stmt->execute();
$row = $rs->fetchArray(SQLITE3_ASSOC);
if (!$row) {
echo json_encode(['success' => true, 'status' => 'none', 'message' => '没有添加成员记录']);
return;
}
$status = $row['status'] ?? 'pending';
$changeData = json_decode($row['change_data'] ?? '{}', true);
$player = $changeData['player'] ?? '';
// ★ 65秒超时自动回滚
if ($status === 'pending') {
$stmtTime = $db->prepare("SELECT created_at FROM web_admin_changes WHERE change_type = 'add_visitor' AND target_name = :name ORDER BY id DESC LIMIT 1");
$stmtTime->bindValue(':name', $name, SQLITE3_TEXT);
$rsTime = $stmtTime->execute();
$timeRow = $rsTime->fetchArray(SQLITE3_ASSOC);
if ($timeRow && (time() - (int)$timeRow['created_at']) > 65) {
// 超时:标记失败
$db->query("UPDATE web_admin_changes SET status = 'failed', acknowledged = 1, acked_at = " . time() . " WHERE change_type = 'add_visitor' AND target_name = '" . $name . "' AND status = 'pending'");
debugLog("handleGetAddVisitorStatus: 添加成员 {$player} → {$name} 超时(超过65秒未处理),已自动标记失败");
echo json_encode(['success' => true, 'status' => 'failed', 'reason' => 'Java端处理超时', 'message' => '添加成员超时,Java端可能未启动或处理延迟']);
return;
}
echo json_encode(['success' => true, 'status' => 'pending', 'message' => '等待Java端验证...']);
} elseif ($status === 'completed') {
echo json_encode(['success' => true, 'status' => 'completed', 'player' => $player, 'message' => '添加成员成功']);
} else {
echo json_encode(['success' => true, 'status' => 'failed', 'message' => '添加成员失败: 玩家可能不存在']);
}
}
/** /**
* 查询过户状态 * 查询过户状态
*/ */
+35 -2
View File
@@ -3075,8 +3075,41 @@ async function addLandVisitor(landName) {
const res = await fetch(url); const res = await fetch(url);
const data = await res.json(); const data = await res.json();
if (data.success) { if (data.success) {
landsState.currentLand = landName; // ★ 参考改领地主流程:异步验证,轮询状态
await renderLandDetail(document.getElementById('content'), landName); if (data.pending) {
glassAlert('添加成员请求已提交,正在验证玩家...');
// 轮询等待验证结果(65秒超时)
let pollCount = 0;
const maxPolls = 13; // 65秒 / 5秒
const pollInterval = setInterval(async () => {
pollCount++;
try {
const statusUrl = new URL(API + 'land_api.php', location.href);
statusUrl.searchParams.set('action', 'get_add_visitor_status');
statusUrl.searchParams.set('name', landName);
statusUrl.searchParams.set('token', TOKEN);
const statusRes = await fetch(statusUrl);
const statusData = await statusRes.json();
if (statusData.status === 'completed') {
clearInterval(pollInterval);
glassAlert('添加成员成功!');
landsState.currentLand = landName;
await renderLandDetail(document.getElementById('content'), landName);
} else if (statusData.status === 'failed') {
clearInterval(pollInterval);
glassAlert('添加成员失败: ' + (statusData.reason || '玩家可能不存在'));
} else if (pollCount >= maxPolls) {
clearInterval(pollInterval);
glassAlert('验证超时,请稍后刷新查看');
}
} catch (e) {
// 继续轮询
}
}, 5000);
} else {
landsState.currentLand = landName;
await renderLandDetail(document.getElementById('content'), landName);
}
} else { } else {
glassAlert(data.error || '操作失败'); glassAlert(data.error || '操作失败');
} }