修复过户面板:移除独立过户按钮改为主改主、adminTransferLand/deleteLand函数修复、改主流程改为pending等待Java验证、添加owner_change_callback回调
This commit is contained in:
@@ -2330,8 +2330,7 @@ async function loadLands(el) {
|
||||
<td style="font-size:11px">${l.x1},${l.z1} → ${l.x2},${l.z2}</td>
|
||||
<td>${size} 格²</td>
|
||||
<td>
|
||||
<button onclick="changeLandOwner('${escAdmHtml(l.name)}','${escAdmHtml(l.owner)}')" style="padding:2px 8px;background:var(--accent);color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">改主</button>
|
||||
<button onclick="adminTransferLand('${escAdmHtml(l.name)}','${escAdmHtml(l.owner)}')" style="padding:2px 8px;background:#ff9800;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">过户</button>
|
||||
<button onclick="adminTransferLand('${escAdmHtml(l.name)}','${escAdmHtml(l.owner)}')" style="padding:2px 8px;background:#ff9800;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">改主</button>
|
||||
<button onclick="deleteLand('${escAdmHtml(l.name)}')" style="padding:2px 8px;background:#f44336;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px">删除</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
@@ -2414,55 +2413,6 @@ async function saveLandConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
async function changeLandOwner(name, currentOwner) {
|
||||
const newOwner = await glassPrompt('领地 [' + name + '] 新所有者', currentOwner||'', '仅允许英文字母、数字和下划线,3-16位');
|
||||
if (newOwner === null || newOwner === false || newOwner === undefined) return;
|
||||
const trimmed = newOwner.trim();
|
||||
if (trimmed === '' || trimmed === currentOwner) return;
|
||||
// 前端验证:Minecraft玩家名格式
|
||||
if (!/^[a-zA-Z0-9_]{3,16}$/.test(trimmed)) {
|
||||
glassAlert('玩家名格式无效:仅允许英文字母、数字和下划线,3-16位');
|
||||
return;
|
||||
}
|
||||
fetch('api/land_api.php?action=update_land_owner', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: 'name=' + encodeURIComponent(name) + '&owner=' + encodeURIComponent(trimmed) + '&secret=sdf1_web_comm_2026_ypshidifu'
|
||||
}).then(r => r.json()).then(d => {
|
||||
if (d.success) {
|
||||
glassAlert(d.message || '改主成功');
|
||||
loadLands(document.getElementById('C'));
|
||||
} else {
|
||||
glassAlert('失败: ' + (d.error||''));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ★ 管理面板过户(带冷却机制)
|
||||
async function adminTransferLand(name, currentOwner) {
|
||||
const newOwner = await glassPrompt('过户领地 [' + name + ']\n新所有者(过户后有1分钟冷却,期间可取消)', currentOwner||'', '仅允许英文字母、数字和下划线,3-16位');
|
||||
if (newOwner === null || newOwner === false || newOwner === undefined) return;
|
||||
const trimmed = newOwner.trim();
|
||||
if (trimmed === '' || trimmed === currentOwner) return;
|
||||
if (!/^[a-zA-Z0-9_]{3,16}$/.test(trimmed)) {
|
||||
glassAlert('玩家名格式无效:仅允许英文字母、数字和下划线,3-16位');
|
||||
return;
|
||||
}
|
||||
if (!await glassConfirm('确认将领地 [' + name + '] 过户给 ' + trimmed + ' ?\n\n过户后1分钟内可以取消')) return;
|
||||
fetch('api/land_api.php?action=transfer_land', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: 'land=' + encodeURIComponent(name) + '&new_owner=' + encodeURIComponent(trimmed) + '&secret=sdf1_web_comm_2026_ypshidifu'
|
||||
}).then(r => r.json()).then(d => {
|
||||
if (d.success) {
|
||||
glassAlert(d.message || '过户成功');
|
||||
loadLands(document.getElementById('C'));
|
||||
} else {
|
||||
glassAlert('失败: ' + (d.error||''));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteLand(name) {
|
||||
if (!await glassConfirm('确定删除领地 [' + name + '] ?')) return;
|
||||
fetch('api/land_api.php?action=delete_land', {
|
||||
@@ -2475,6 +2425,37 @@ async function deleteLand(name) {
|
||||
});
|
||||
}
|
||||
|
||||
async function adminTransferLand(name, currentOwner) {
|
||||
const newOwner = prompt('将领地 [' + name + '] 改主给谁?\n当前所有者: ' + (currentOwner||'无'), '');
|
||||
if (!newOwner || !newOwner.trim()) return;
|
||||
const trimmed = newOwner.trim();
|
||||
if (!/^[a-zA-Z0-9_]{3,16}$/.test(trimmed)) {
|
||||
glassAlert('玩家名格式不正确,仅支持英文字母、数字和下划线(3-16位)');
|
||||
return;
|
||||
}
|
||||
if (trimmed === currentOwner) {
|
||||
glassAlert('新旧所有者相同,无需更改');
|
||||
return;
|
||||
}
|
||||
if (!await glassConfirm('确定将领地 [' + name + '] 改主为 [' + trimmed + '] ?\n(将写入待验证队列,等Java端确认后生效)')) return;
|
||||
try {
|
||||
const res = await fetch('api/land_api.php?action=update_land_owner', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: 'name=' + encodeURIComponent(name) + '&owner=' + encodeURIComponent(trimmed) + '&admin_token=' + (admToken||'')
|
||||
});
|
||||
const d = await res.json();
|
||||
if (d.success) {
|
||||
glassAlert(d.message || '已提交改主请求,等待Java端验证');
|
||||
loadLands(document.getElementById('C'));
|
||||
} else {
|
||||
glassAlert('失败: ' + (d.error || ''));
|
||||
}
|
||||
} catch(e) {
|
||||
glassAlert('请求失败: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteShopItem(id) {
|
||||
if (!await glassConfirm('确定下架商品 #' + id + ' ?')) return;
|
||||
fetch('api/land_api.php?action=delete_shop_item', {
|
||||
|
||||
+92
-23
@@ -92,7 +92,7 @@ try {
|
||||
// 同步类action只接受secret验证(Java端推送)
|
||||
$syncActions = ['sync_lands', 'sync_shop', 'sync_permissions', 'get_pending_validations', 'validation_callback'];
|
||||
// 管理面板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', 'get_player_groups'];
|
||||
$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_player_groups', 'transfer_land_admin'];
|
||||
// 玩家端action:需要token
|
||||
$playerActions = ['my_lands', 'land_detail', 'add_visitor', 'remove_visitor', 'list_visitors', 'land_shop', 'buy_permission', 'transfer_land', 'cancel_transfer', 'transfer_status'];
|
||||
// ★ 玩家端领地字段更新(效果管理、开关等)
|
||||
@@ -102,7 +102,7 @@ try {
|
||||
// ★ 成员独立权限操作action(领地所有者编辑成员权限)
|
||||
$memberPermActions = ['get_member_perms', 'update_member_perm', 'clear_member_perm'];
|
||||
// ★ Java端轮询PHP管理员变更 + Java回调过户结果
|
||||
$syncFromPhpActions = ['poll_admin_changes', 'ack_admin_changes', 'transfer_callback'];
|
||||
$syncFromPhpActions = ['poll_admin_changes', 'ack_admin_changes', 'transfer_callback', 'owner_change_callback'];
|
||||
|
||||
if (in_array($action, $syncActions) || in_array($action, $syncFromPhpActions)) {
|
||||
// 同步类:必须有secret
|
||||
@@ -201,11 +201,16 @@ try {
|
||||
handleDeleteLand($db, $_POST);
|
||||
break;
|
||||
|
||||
// ===== 管理面板:更新领地所有者 =====
|
||||
// ===== 管理面板:更新领地所有者(含过户冷却机制)=====
|
||||
case 'update_land_owner':
|
||||
handleUpdateLandOwner($db, $_POST);
|
||||
break;
|
||||
|
||||
// ===== 管理面板:领地过户(管理员专用,带冷却机制)=====
|
||||
case 'transfer_land_admin':
|
||||
handleTransferLandAdmin($db, $_POST);
|
||||
break;
|
||||
|
||||
// ===== 管理面板:删除权限商品 =====
|
||||
case 'delete_shop_item':
|
||||
handleDeleteShopItem($db, $_POST);
|
||||
@@ -354,6 +359,11 @@ try {
|
||||
handleTransferCallback($db, $_POST + $_GET);
|
||||
break;
|
||||
|
||||
// ===== Java端:管理面板改主结果回调 =====
|
||||
case 'owner_change_callback':
|
||||
handleOwnerChangeCallback($db, $_POST + $_GET);
|
||||
break;
|
||||
|
||||
// ===== 用户组管理 =====
|
||||
case 'list_user_groups':
|
||||
handleListUserGroups($db);
|
||||
@@ -525,9 +535,15 @@ function initLandTables($db) {
|
||||
change_data TEXT DEFAULT '',
|
||||
created_at INTEGER DEFAULT 0,
|
||||
acknowledged INTEGER DEFAULT 0,
|
||||
acked_at INTEGER DEFAULT 0
|
||||
acked_at INTEGER DEFAULT 0,
|
||||
status TEXT DEFAULT 'done'
|
||||
)");
|
||||
|
||||
// ★ 为已有表添加status字段(如果不存在)
|
||||
try {
|
||||
$db->exec("ALTER TABLE web_admin_changes ADD COLUMN status TEXT DEFAULT 'done'");
|
||||
} catch (\Throwable $e) { /* 字段已存在 */ }
|
||||
|
||||
// ★ 领地所有者变更表(记录PHP端修改的所有者变更)
|
||||
$db->exec("CREATE TABLE IF NOT EXISTS web_land_owner_changes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -791,7 +807,7 @@ function handleUpdateLandOwner($db, $post) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 管理面板改主:不走异步验证,直接写入变更队列让Java端执行
|
||||
// ★ 查询领地信息
|
||||
$stmt = $db->prepare("SELECT id, owner FROM web_area_lands WHERE name = :name");
|
||||
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
@@ -808,31 +824,26 @@ function handleUpdateLandOwner($db, $post) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新PHP本地副本
|
||||
$stmt2 = $db->prepare("UPDATE web_area_lands SET owner = :owner WHERE name = :name");
|
||||
$stmt2->bindValue(':owner', $owner, SQLITE3_TEXT);
|
||||
$stmt2->bindValue(':name', $name, SQLITE3_TEXT);
|
||||
$stmt2->execute();
|
||||
// ★ 检查是否已有pending的owner_change(防止重复提交)
|
||||
$stmtCheck = $db->prepare("SELECT id FROM web_admin_changes WHERE change_type = 'owner_change' AND target_name = :name AND status = 'pending'");
|
||||
$stmtCheck->bindValue(':name', $name, SQLITE3_TEXT);
|
||||
$checkResult = $stmtCheck->execute();
|
||||
if ($checkResult->fetchArray(SQLITE3_ASSOC)) {
|
||||
echo json_encode(['success' => false, 'error' => '该领地已有待验证的改主请求,请等待Java端处理']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录变更
|
||||
$stmt3 = $db->prepare("INSERT INTO web_land_owner_changes (land_id, land_name, old_owner, new_owner, created_at) VALUES (:lid, :name, :old, :new, :now)");
|
||||
$stmt3->bindValue(':lid', $landId, SQLITE3_INTEGER);
|
||||
$stmt3->bindValue(':name', $name, SQLITE3_TEXT);
|
||||
$stmt3->bindValue(':old', $oldOwner, SQLITE3_TEXT);
|
||||
$stmt3->bindValue(':new', $owner, SQLITE3_TEXT);
|
||||
$stmt3->bindValue(':now', time(), SQLITE3_INTEGER);
|
||||
$stmt3->execute();
|
||||
|
||||
// ★ 写入web_admin_changes让Java端pollAdminChanges拉取并执行
|
||||
$stmt4 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at) VALUES ('owner_change', :id, :name, :data, :now)");
|
||||
// ★ 不再直接更新PHP本地副本!等Java端验证并回调后再更新
|
||||
// 写入web_admin_changes让Java端pollAdminChanges拉取并验证执行
|
||||
$stmt4 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at, status) VALUES ('owner_change', :id, :name, :data, :now, 'pending')");
|
||||
$stmt4->bindValue(':id', $landId, SQLITE3_INTEGER);
|
||||
$stmt4->bindValue(':name', $name, SQLITE3_TEXT);
|
||||
$stmt4->bindValue(':data', json_encode(['old_owner' => $oldOwner, 'new_owner' => $owner, 'source' => 'admin_panel']), SQLITE3_TEXT);
|
||||
$stmt4->bindValue(':now', time(), SQLITE3_INTEGER);
|
||||
$stmt4->execute();
|
||||
|
||||
debugLog("handleUpdateLandOwner: 管理面板改主 {$name}: {$oldOwner} → {$owner}");
|
||||
echo json_encode(['success' => true, 'message' => "领地 [{$name}] 所有者已从 {$oldOwner} 变更为 {$owner}"]);
|
||||
debugLog("handleUpdateLandOwner: 管理面板改主 {$name}: {$oldOwner} → {$owner} (已写入pending队列,等待Java验证)");
|
||||
echo json_encode(['success' => true, 'pending' => true, 'message' => "改主请求已提交: [{$name}] {$oldOwner} → {$owner},等待Java端验证后生效"]);
|
||||
}
|
||||
|
||||
function handleDeleteShopItem($db, $post) {
|
||||
@@ -2141,6 +2152,64 @@ function handleTransferCallback($db, $post) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ★ Java端管理面板改主结果回调
|
||||
* 验证通过→更新PHP本地副本+记录变更;验证失败→标记失败
|
||||
*/
|
||||
function handleOwnerChangeCallback($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的owner_change记录
|
||||
$stmt = $db->prepare("SELECT * FROM web_admin_changes WHERE id = :id AND change_type = 'owner_change' 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);
|
||||
$newOwner = $changeData['new_owner'] ?? '';
|
||||
$oldOwner = $changeData['old_owner'] ?? '';
|
||||
|
||||
if ($success) {
|
||||
// ★ Java验证通过:更新PHP本地副本
|
||||
$stmtUpd = $db->prepare("UPDATE web_area_lands SET owner = :owner WHERE name = :name");
|
||||
$stmtUpd->bindValue(':owner', $newOwner, SQLITE3_TEXT);
|
||||
$stmtUpd->bindValue(':name', $targetName, SQLITE3_TEXT);
|
||||
$stmtUpd->execute();
|
||||
|
||||
// 记录变更历史
|
||||
$landId = (int)($row['target_id'] ?? 0);
|
||||
$stmtChg = $db->prepare("INSERT INTO web_land_owner_changes (land_id, land_name, old_owner, new_owner, created_at) VALUES (:lid, :name, :old, :new, :now)");
|
||||
$stmtChg->bindValue(':lid', $landId, SQLITE3_INTEGER);
|
||||
$stmtChg->bindValue(':name', $targetName, SQLITE3_TEXT);
|
||||
$stmtChg->bindValue(':old', $oldOwner, SQLITE3_TEXT);
|
||||
$stmtChg->bindValue(':new', $newOwner, SQLITE3_TEXT);
|
||||
$stmtChg->bindValue(':now', time(), SQLITE3_INTEGER);
|
||||
$stmtChg->execute();
|
||||
|
||||
// 标记处理完成
|
||||
$db->query("UPDATE web_admin_changes SET status = 'completed', acknowledged = 1, acked_at = " . time() . " WHERE id = " . $changeId);
|
||||
debugLog("handleOwnerChangeCallback: 改主 {$targetName} {$oldOwner}→{$newOwner} 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("handleOwnerChangeCallback: 改主 {$targetName} {$oldOwner}→{$newOwner} Java验证失败: {$reason}");
|
||||
echo json_encode(['success' => true, 'message' => "改主验证失败: {$reason}"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询过户状态
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user