Commit cd36b1ec authored by Nemo Ma's avatar Nemo Ma

FEAT: Add Database Master/Slave management

parent 31b2f0c9
......@@ -96,6 +96,31 @@
$salt = 'a29d8868b409591398e6ccca95f968ef';
// ============================================================================
// 主从数据库配置 (Master-Slave Database Configuration)
// 以下配置项不能通过游戏后台修改,只能手动编辑或通过安装脚本配置
$slave_level = 0; // 从服务器级别: 0=主服务器, 1=从服务器(手动迁移), 2=从服务器(自动同步), 3=从服务器(直接使用主数据库)
// Slave level: 0=master, 1=slave(manual migration), 2=slave(auto sync), 3=slave(direct master db)
$master_dbhost = ''; // 主数据库服务器地址 (仅在slave_level>0时有效)
// Master database server (only valid when slave_level>0)
$master_dbuser = ''; // 主数据库用户名 (仅在slave_level>0时有效)
// Master database username (only valid when slave_level>0)
$master_dbpw = ''; // 主数据库密码 (仅在slave_level>0时有效)
// Master database password (only valid when slave_level>0)
$master_dbname = ''; // 主数据库名 (仅在slave_level>0时有效)
// Master database name (only valid when slave_level>0)
$master_tablepre = ''; // 主数据库表前缀 (仅在slave_level>0时有效)
// Master database table prefix (only valid when slave_level>0)
$master_server_name = ''; // 主服务器名称 (用于显示)
// Master server name (for display)
// ============================================================================
?>
\ No newline at end of file
日期格式显示修复记录
时间戳:2025-01-12
## 问题描述
在用户账户界面显示已绑定账号信息时,日期没有被正确转换,显示为:
已绑定主服务器账户:Amarillo_NMC (上次同步:{date('Y-m-d H:i:s', 1749481373)})
## 问题原因
在模板文件 templates/default/user.htm 中,PHP的 date() 函数没有被正确执行,而是被当作字符串输出。这是因为模板系统不能直接执行复杂的PHP函数调用。
## 解决方案
1. 在 user.php 中预先格式化日期
2. 将格式化后的日期作为新的数组元素传递给模板
3. 在模板中直接使用格式化后的日期字符串
## 修改的文件
### 1. user.php
- 在获取用户同步状态后,添加日期格式化逻辑
- 将格式化后的日期存储在 $user_sync_status['sync_time_formatted'] 中
### 2. templates/default/user.htm
- 将模板中的 {date('Y-m-d H:i:s', $user_sync_status['sync_time'])}
- 改为 {$user_sync_status['sync_time_formatted']}
## 修复后的效果
现在会正确显示:
已绑定主服务器账户:Amarillo_NMC (上次同步:2025-01-12 15:30:45)
## 技术说明
这种处理方式符合MVC模式的最佳实践:
- 数据处理在控制器(PHP文件)中完成
- 模板只负责显示已处理好的数据
- 避免在模板中执行复杂的PHP逻辑
## 测试建议
1. 确保用户有同步记录的情况下,日期显示正确
2. 确保没有同步记录的用户不会出现错误
3. 验证日期格式符合预期(YYYY-MM-DD HH:MM:SS)
修复管理页面主从服务器配置信息显示问题
时间:2025年1月12日
问题描述:
在管理页面中,主从数据库配置信息显示为空白,用户无法看到当前的主从配置状态。
问题原因:
1. 在 include/admin/configmng.php 中,$slave_config_display 数组只在 $command == 'edit' 条件内定义
2. 当页面首次加载时(没有提交表单),$command 不等于 'edit',导致数组未定义
3. 模板中使用的 $slave_config_display['key'] 语法在 PHP 模板系统中无法正确解析
修复方案:
1. 将主从配置变量的定义移到条件判断外,确保总是可用
2. 将数组形式改为单独的变量,便于模板系统解析
3. 修改模板文件,使用新的变量名
修改文件:
1. include/admin/configmng.php
- 将主从配置变量定义移到 if($command == 'edit') 条件外
- 改用单独变量:$display_slave_level, $display_master_server_name 等
- 添加 isset() 检查,避免未定义变量错误
2. templates/default/admin_configmng.htm
- 将所有 $slave_config_display['key'] 改为对应的 $display_key 变量
修复效果:
- 管理页面现在能正确显示主从数据库配置信息
- 配置信息以只读形式显示,符合安全要求
- 即使在首次加载页面时也能正常显示配置信息
技术要点:
- PHP 模板系统不支持复杂的数组语法,需要使用简单变量
- 使用 isset() 检查避免未定义变量导致的错误
- 保持配置信息的只读特性,防止意外修改
主从数据库同步功能实现记录
时间戳:2025-01-12
## 功能概述
实现了游戏的主从数据库同步功能,支持四种不同的从服务器级别:
- slave_level = 0:主服务器,正常处理,无变化
- slave_level = 1:从服务器,手动迁移用户和成就数据
- slave_level = 2:从服务器,自动同步用户数据
- slave_level = 3:从服务器,直接使用主数据库
## 实现的文件修改
### 1. 配置文件修改
- **config.inc.php**:添加了主从配置项
- $slave_level:从服务器级别
- $master_dbhost:主数据库服务器地址
- $master_dbuser:主数据库用户名
- $master_dbpw:主数据库密码
- $master_dbname:主数据库名
- $master_tablepre:主数据库表前缀
- $master_server_name:主服务器名称
### 2. 安装脚本修改
- **install.php**:
- 添加了主从配置的表单字段
- 添加了主从配置的默认值初始化
- 添加了主从配置的保存处理逻辑
### 3. 管理界面修改
- **include/admin/configmng.php**:添加了主从配置的显示变量
- **templates/default/admin_configmng.htm**:添加了主从配置的只读显示界面
### 4. 核心功能文件
- **include/masterslave.func.php**:新建文件,包含所有主从同步的核心功能
- connect_master_db():连接主数据库
- check_user_in_master():检查用户是否在主数据库中存在
- sync_user_from_master():从主数据库同步用户数据
- get_user_sync_info():获取用户同步信息
- set_user_sync_info():设置用户同步信息
- create_sync_table_if_not_exists():创建同步表
- get_user_sync_status():获取用户同步状态
- should_auto_sync():检查是否需要自动同步
- should_use_master_db():检查是否直接使用主数据库
### 5. 用户界面修改
- **user.php**:
- 添加了主从同步功能的引入
- 添加了sync_master模式的处理逻辑
- 添加了主从同步相关的显示变量
- **templates/default/user.htm**:
- 添加了主从数据库同步功能的界面
- 包含用户名密码输入框
- 包含同步按钮和JavaScript处理函数
- 显示已绑定账户信息和注意事项
### 6. 登录逻辑修改
- **login.php**:
- 添加了主从同步功能的引入
- 实现了slave_level=2时的自动同步逻辑
- 当用户不存在时自动尝试从主数据库同步
### 7. 核心系统修改
- **include/common.inc.php**:
- 修改了数据库连接逻辑
- 实现了slave_level=3时直接使用主数据库
- 正确设置了$gtablepre变量
### 8. 测试工具
- **test_masterslave.php**:新建测试脚本
- 显示当前主从配置状态
- 测试主数据库连接
- 检查同步表状态
- 提供同步功能测试界面
## 数据库表结构
自动创建的同步表:{$gtablepre}user_sync
- id:自增主键
- target_username:目标用户名(从服务器用户名)
- master_username:主服务器用户名
- sync_time:同步时间戳
## 功能特点
1. **安全性**:
- 需要验证主服务器的用户名和密码
- 每个主服务器账户只能绑定一个从服务器账户
- 防止重复同步和冲突
2. **灵活性**:
- 支持手动迁移和自动同步两种模式
- 可配置不同的从服务器级别
- 支持显示同步状态和历史
3. **用户体验**:
- 清晰的界面提示和操作指导
- 同步状态的实时反馈
- 管理界面的只读显示
4. **数据完整性**:
- 同步用户的积分、成就、头衔等关键数据
- 保持数据的一致性和完整性
- 记录同步历史和状态
## 注意事项
1. 主从配置不能通过管理界面修改,只能通过config.inc.php文件或安装脚本配置
2. slave_level=3的直接使用主数据库功能需要进一步实现
3. 需要确保主从服务器之间的网络连接稳定
4. 建议在生产环境中谨慎使用自动同步功能
## 后续扩展
1. 实现slave_level=3的直接主数据库访问
2. 添加更多的同步选项和过滤条件
3. 实现双向同步功能
4. 添加同步日志和监控功能
......@@ -3,8 +3,18 @@ if(!defined('IN_ADMIN')) {
exit('Access Denied');
}
// 主从配置只显示,不允许修改(移到条件外,确保总是可用)
// 为模板提供单独的变量
$display_slave_level = isset($slave_level) ? $slave_level : '';
$display_master_server_name = isset($master_server_name) ? $master_server_name : '';
$display_master_dbhost = isset($master_dbhost) ? $master_dbhost : '';
$display_master_dbuser = isset($master_dbuser) ? $master_dbuser : '';
$display_master_dbpw = isset($master_dbpw) ? $master_dbpw : '';
$display_master_dbname = isset($master_dbname) ? $master_dbname : '';
$display_master_tablepre = isset($master_tablepre) ? $master_tablepre : '';
if($command == 'edit') {
$ednum = 0;
$edfmt = Array('authkey'=>'','bbsurl'=>'','gameurl'=>'','homepage'=>'','moveut'=>'int','moveutmin'=>'int','tplrefresh'=>'b','errorinfo'=>'b');
$edlist = Array();
......
......@@ -39,7 +39,15 @@ list($sec,$min,$hour,$day,$month,$year,$wday) = explode(',',date("s,i,H,j,n,Y,w"
require GAME_ROOT.'./include/db_'.$database.'.class.php';
$db = new dbstuff;
$db->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect);
// 检查是否直接使用主数据库 (slave_level = 3)
if($slave_level == 3 && !empty($master_dbhost) && !empty($master_dbuser) && !empty($master_dbname)) {
$db->connect($master_dbhost, $master_dbuser, $master_dbpw, $master_dbname, $pconnect);
$gtablepre = $master_tablepre;
} else {
$db->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect);
$gtablepre = $tablepre;
}
//$db->select_db($dbname);
unset($dbhost, $dbuser, $dbpw, $dbname, $pconnect);
......@@ -60,7 +68,10 @@ require config('audio',$gamecfg);
require config('tooltip',$gamecfg);
require config('titles',$gamecfg);
$gtablepre = $tablepre;
// $gtablepre 已在数据库连接时设置,这里不再重新赋值
if(!isset($gtablepre)) {
$gtablepre = $tablepre;
}
if($need_update_db_structrue) roommng_verify_db_game_structure();
......
<?php
if(!defined('IN_GAME')) {
exit('Access Denied');
}
/**
* 主从数据库同步功能
* Master-Slave Database Synchronization Functions
*/
/**
* 连接主数据库
* Connect to master database
*/
function connect_master_db() {
global $master_dbhost, $master_dbuser, $master_dbpw, $master_dbname, $database, $pconnect;
if(empty($master_dbhost) || empty($master_dbuser) || empty($master_dbname)) {
return false;
}
require_once GAME_ROOT.'./include/db_'.$database.'.class.php';
$master_db = new dbstuff;
$master_db->connect($master_dbhost, $master_dbuser, $master_dbpw, $master_dbname, $pconnect);
if($master_db->error()) {
return false;
}
return $master_db;
}
/**
* 检查用户是否已在主数据库中存在
* Check if user exists in master database
*/
function check_user_in_master($username, $password) {
global $master_tablepre;
$master_db = connect_master_db();
if(!$master_db) {
return false;
}
$username = addslashes($username);
$password = addslashes($password);
$result = $master_db->query("SELECT * FROM {$master_tablepre}users WHERE username = '$username' AND password = '$password'");
if(!$master_db->num_rows($result)) {
return false;
}
return $master_db->fetch_array($result);
}
/**
* 从主数据库同步用户数据到本地数据库
* Sync user data from master database to local database
*/
function sync_user_from_master($username, $password, $target_username = null) {
global $db, $gtablepre, $master_tablepre;
$master_db = connect_master_db();
if(!$master_db) {
return array('success' => false, 'message' => '无法连接到主数据库');
}
$username = addslashes($username);
$password = addslashes($password);
// 检查主数据库中的用户
$result = $master_db->query("SELECT * FROM {$master_tablepre}users WHERE username = '$username' AND password = '$password'");
if(!$master_db->num_rows($result)) {
return array('success' => false, 'message' => '主数据库中未找到匹配的用户名和密码');
}
$master_user_data = $master_db->fetch_array($result);
$target_username = $target_username ? $target_username : $username;
$target_username = addslashes($target_username);
// 检查是否已经被其他账户同步过
$sync_info = get_user_sync_info($username);
if($sync_info && $sync_info['target_username'] != $target_username) {
return array('success' => false, 'message' => "该主服务器账户已被用户 {$sync_info['target_username']} 同步,无法重复同步");
}
// 检查本地是否已存在目标用户
$local_result = $db->query("SELECT * FROM {$gtablepre}users WHERE username = '$target_username'");
if($db->num_rows($local_result)) {
$local_user_data = $db->fetch_array($local_result);
// 更新现有用户数据
$update_fields = array();
$sync_fields = array('credits', 'credits2', 'achievement', 'achrev', 'daily', 'nick', 'nicks', 'validgames', 'wingames', 'gender', 'icon', 'club', 'motto', 'killmsg', 'lastword');
foreach($sync_fields as $field) {
if(isset($master_user_data[$field])) {
$value = addslashes($master_user_data[$field]);
$update_fields[] = "`$field` = '$value'";
}
}
if(!empty($update_fields)) {
$update_sql = "UPDATE {$gtablepre}users SET " . implode(', ', $update_fields) . " WHERE username = '$target_username'";
$db->query($update_sql);
}
// 记录同步信息
set_user_sync_info($target_username, $username);
return array('success' => true, 'message' => '用户数据同步成功');
} else {
// 创建新用户
$insert_fields = array('username' => $target_username);
$sync_fields = array('password', 'groupid', 'credits', 'credits2', 'achievement', 'achrev', 'daily', 'nick', 'nicks', 'validgames', 'wingames', 'gender', 'icon', 'club', 'motto', 'killmsg', 'lastword');
foreach($sync_fields as $field) {
if(isset($master_user_data[$field])) {
$insert_fields[$field] = addslashes($master_user_data[$field]);
}
}
$fields = implode('`, `', array_keys($insert_fields));
$values = "'" . implode("', '", array_values($insert_fields)) . "'";
$insert_sql = "INSERT INTO {$gtablepre}users (`$fields`) VALUES ($values)";
$db->query($insert_sql);
if($db->affected_rows()) {
// 记录同步信息
set_user_sync_info($target_username, $username);
return array('success' => true, 'message' => '用户账户创建并同步成功');
} else {
return array('success' => false, 'message' => '创建用户账户失败');
}
}
}
/**
* 获取用户同步信息
* Get user sync information
*/
function get_user_sync_info($master_username) {
global $db, $gtablepre;
$master_username = addslashes($master_username);
$result = $db->query("SELECT * FROM {$gtablepre}user_sync WHERE master_username = '$master_username'");
if($db->num_rows($result)) {
return $db->fetch_array($result);
}
return false;
}
/**
* 设置用户同步信息
* Set user sync information
*/
function set_user_sync_info($target_username, $master_username) {
global $db, $gtablepre;
$target_username = addslashes($target_username);
$master_username = addslashes($master_username);
$sync_time = time();
// 检查同步表是否存在,不存在则创建
create_sync_table_if_not_exists();
// 检查是否已存在记录
$result = $db->query("SELECT * FROM {$gtablepre}user_sync WHERE target_username = '$target_username'");
if($db->num_rows($result)) {
// 更新现有记录
$db->query("UPDATE {$gtablepre}user_sync SET master_username = '$master_username', sync_time = '$sync_time' WHERE target_username = '$target_username'");
} else {
// 插入新记录
$db->query("INSERT INTO {$gtablepre}user_sync (target_username, master_username, sync_time) VALUES ('$target_username', '$master_username', '$sync_time')");
}
}
/**
* 创建同步表(如果不存在)
* Create sync table if not exists
*/
function create_sync_table_if_not_exists() {
global $db, $gtablepre;
$table_name = $gtablepre . 'user_sync';
// 检查表是否存在
$result = $db->query("SHOW TABLES LIKE '$table_name'", 'SILENT');
if(!$db->num_rows($result)) {
// 创建同步表
$create_sql = "CREATE TABLE `$table_name` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`target_username` char(15) NOT NULL DEFAULT '',
`master_username` char(15) NOT NULL DEFAULT '',
`sync_time` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `target_username` (`target_username`),
KEY `master_username` (`master_username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8";
$db->query($create_sql);
}
}
/**
* 获取用户的同步状态
* Get user sync status
*/
function get_user_sync_status($username) {
global $db, $gtablepre;
$username = addslashes($username);
$result = $db->query("SELECT * FROM {$gtablepre}user_sync WHERE target_username = '$username'");
if($db->num_rows($result)) {
return $db->fetch_array($result);
}
return false;
}
/**
* 检查是否为从服务器且需要自动同步
* Check if this is a slave server that needs auto sync
*/
function should_auto_sync() {
global $slave_level;
return ($slave_level == 2);
}
/**
* 检查是否直接使用主数据库
* Check if should use master database directly
*/
function should_use_master_db() {
global $slave_level;
return ($slave_level == 3);
}
?>
......@@ -245,6 +245,15 @@ if(!$action) {
$moveut = 0;
$gamefounder = 'admin';
// 主从配置默认值
$slave_level = 0;
$master_server_name = '';
$master_dbhost = '';
$master_dbuser = '';
$master_dbpw = '';
$master_dbname = '';
$master_tablepre = '';
@include './config.inc.php';
$now = time();
list($nowsec,$nowmin,$nowhour,$nowday,$nowmonth,$nowyear,$nowwday,$nowyday,$nowisdst) = localtime($now);
......@@ -311,6 +320,51 @@ if(!$action) {
<td bgcolor="#EEEEF6" align="center"><input type="text" name="gameurl" value="<?php echo $gameurl; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;<?php echo $lang['gameurl_comment']; ?></td>
</tr>
<tr>
<td bgcolor="#E3E3EA" colspan="3" align="center" style="color: #FF0000; font-weight: bold;">主从数据库配置 (Master-Slave Database Configuration)</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;从服务器级别</td>
<td bgcolor="#EEEEF6" align="center">
<select name="slave_level" size="1">
<option value="0"<?php echo (!isset($slave_level) || $slave_level == 0) ? ' selected' : ''; ?>>0 - 主服务器</option>
<option value="1"<?php echo (isset($slave_level) && $slave_level == 1) ? ' selected' : ''; ?>>1 - 从服务器(手动迁移)</option>
<option value="2"<?php echo (isset($slave_level) && $slave_level == 2) ? ' selected' : ''; ?>>2 - 从服务器(自动同步)</option>
<option value="3"<?php echo (isset($slave_level) && $slave_level == 3) ? ' selected' : ''; ?>>3 - 从服务器(直接使用主数据库)</option>
</select>
</td>
<td bgcolor="#E3E3EA">&nbsp;0=主服务器, 1=手动迁移, 2=自动同步, 3=直接使用主数据库</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;主服务器名称</td>
<td bgcolor="#EEEEF6" align="center"><input type="text" name="master_server_name" value="<?php echo isset($master_server_name) ? $master_server_name : ''; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;用于显示的主服务器名称</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;主数据库服务器</td>
<td bgcolor="#EEEEF6" align="center"><input type="text" name="master_dbhost" value="<?php echo isset($master_dbhost) ? $master_dbhost : ''; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;主数据库服务器地址 (仅从服务器需要)</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;主数据库用户名</td>
<td bgcolor="#EEEEF6" align="center"><input type="text" name="master_dbuser" value="<?php echo isset($master_dbuser) ? $master_dbuser : ''; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;主数据库用户名 (仅从服务器需要)</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;主数据库密码</td>
<td bgcolor="#EEEEF6" align="center"><input type="password" name="master_dbpw" value="<?php echo isset($master_dbpw) ? $master_dbpw : ''; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;主数据库密码 (仅从服务器需要)</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;主数据库名</td>
<td bgcolor="#EEEEF6" align="center"><input type="text" name="master_dbname" value="<?php echo isset($master_dbname) ? $master_dbname : ''; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;主数据库名 (仅从服务器需要)</td>
</tr>
<tr>
<td bgcolor="#E3E3EA">&nbsp;主数据库表前缀</td>
<td bgcolor="#EEEEF6" align="center"><input type="text" name="master_tablepre" value="<?php echo isset($master_tablepre) ? $master_tablepre : ''; ?>" size="30"></td>
<td bgcolor="#E3E3EA">&nbsp;主数据库表前缀 (仅从服务器需要)</td>
</tr>
</table>
<br>
<input type="hidden" name="action" value="dbselect">
......@@ -496,7 +550,17 @@ if(!$action) {
$bbsurl = setconfig($_POST['bbsurl']);
$gameurl = setconfig($_POST['gameurl']);
$moveut = (int)$_POST['moveut'];
$salt = bin2hex(random_bytes(16));
// 主从配置处理
$slave_level = (int)$_POST['slave_level'];
$master_server_name = setconfig($_POST['master_server_name']);
$master_dbhost = setconfig($_POST['master_dbhost']);
$master_dbuser = setconfig($_POST['master_dbuser']);
$master_dbpw = setconfig($_POST['master_dbpw']);
$master_dbname = setconfig($_POST['master_dbname']);
$master_tablepre = setconfig($_POST['master_tablepre']);
$salt = bin2hex(random_bytes(16));
$fp = fopen('./config.inc.php', 'r');
$configfile = fread($fp, filesize('./config.inc.php'));
......@@ -511,6 +575,16 @@ if(!$action) {
$configfile = preg_replace("/[$]bbsurl\s*\=\s*[\"'].*?[\"'];/is", "\$bbsurl = '$bbsurl';", $configfile);
$configfile = preg_replace("/[$]gameurl\s*\=\s*[\"'].*?[\"'];/is", "\$gameurl = '$gameurl';", $configfile);
$configfile = preg_replace("/[$]moveut\s*\=\s*-?[0-9]+;/is", "\$moveut = $moveut;", $configfile);
// 主从配置更新
$configfile = preg_replace("/[$]slave_level\s*\=\s*-?[0-9]+;/is", "\$slave_level = $slave_level;", $configfile);
$configfile = preg_replace("/[$]master_server_name\s*\=\s*[\"'].*?[\"'];/is", "\$master_server_name = '$master_server_name';", $configfile);
$configfile = preg_replace("/[$]master_dbhost\s*\=\s*[\"'].*?[\"'];/is", "\$master_dbhost = '$master_dbhost';", $configfile);
$configfile = preg_replace("/[$]master_dbuser\s*\=\s*[\"'].*?[\"'];/is", "\$master_dbuser = '$master_dbuser';", $configfile);
$configfile = preg_replace("/[$]master_dbpw\s*\=\s*[\"'].*?[\"'];/is", "\$master_dbpw = '$master_dbpw';", $configfile);
$configfile = preg_replace("/[$]master_dbname\s*\=\s*[\"'].*?[\"'];/is", "\$master_dbname = '$master_dbname';", $configfile);
$configfile = preg_replace("/[$]master_tablepre\s*\=\s*[\"'].*?[\"'];/is", "\$master_tablepre = '$master_tablepre';", $configfile);
$configfile = preg_replace("/[$]salt\s*\=\s*[\"'].*?[\"'];/is", "\$salt = '$salt';", $configfile);
......
......@@ -3,6 +3,7 @@
define('CURSCRIPT', 'login');
require './include/common.inc.php';
require './include/masterslave.func.php';
//error_reporting(E_ERROR);
//set_magic_quotes_runtime(0);
......@@ -118,9 +119,42 @@ $gender = 0;
$result = $db->query("SELECT * FROM {$gtablepre}users WHERE username = '$username'");
if(!$db->num_rows($result)) {
gexit($_ERROR['user_not_exists'],__file__,__line__);
//$groupid = 1;
//$db->query("INSERT INTO {$gtablepre}users (username,`password`,groupid,ip,credits,gender) VALUES ('$username', '$password', '$groupid', '$onlineip', '$credits', '$gender')");
// 如果是从服务器且配置了自动同步,尝试从主数据库同步用户
if(should_auto_sync()) {
$original_password = '';
// 从POST或其他方式获取原始密码
foreach($_POST as $key => $value) {
if($key == 'password') {
$original_password = $value;
break;
}
}
if($original_password) {
$sync_result = sync_user_from_master($username, $original_password, $username);
if($sync_result['success']) {
// 同步成功,重新查询用户数据
$result = $db->query("SELECT * FROM {$gtablepre}users WHERE username = '$username'");
if($db->num_rows($result)) {
$userdata = $db->fetch_array($result);
// 验证密码
if($userdata['password'] != $password) {
gexit($_ERROR['wrong_pw'],__file__,__line__);
}
// 添加同步成功的提示信息
gsetcookie('sync_message', '账户数据已从主服务器自动同步', 60);
} else {
gexit($_ERROR['user_not_exists'],__file__,__line__);
}
} else {
gexit($_ERROR['user_not_exists'],__file__,__line__);
}
} else {
gexit($_ERROR['user_not_exists'],__file__,__line__);
}
} else {
gexit($_ERROR['user_not_exists'],__file__,__line__);
}
} else {
$userdata = $db->fetch_array($result);
if($userdata['groupid'] <= 0){
......
......@@ -43,6 +43,48 @@
<td><input type="text" name="homepage" value="$homepage" size="30"></td>
<td>$lang['homepage_comment']</td>
</tr>
<tr>
<td colspan="3" style="background-color: #FFE4E1; text-align: center; font-weight: bold; color: #8B0000;">主从数据库配置 (只读显示)</td>
</tr>
<tr>
<td>从服务器级别</td>
<td><input type="text" value="$display_slave_level" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>0=主服务器, 1=从服务器(手动迁移), 2=从服务器(自动同步), 3=从服务器(直接使用主数据库)</td>
</tr>
<tr>
<td>主服务器名称</td>
<td><input type="text" value="$display_master_server_name" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>用于显示的主服务器名称</td>
</tr>
<tr>
<td>主数据库服务器</td>
<td><input type="text" value="$display_master_dbhost" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>主数据库服务器地址</td>
</tr>
<tr>
<td>主数据库用户名</td>
<td><input type="text" value="$display_master_dbuser" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>主数据库用户名</td>
</tr>
<tr>
<td>主数据库密码</td>
<td><input type="password" value="$display_master_dbpw" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>主数据库密码</td>
</tr>
<tr>
<td>主数据库名</td>
<td><input type="text" value="$display_master_dbname" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>主数据库名</td>
</tr>
<tr>
<td>主数据库表前缀</td>
<td><input type="text" value="$display_master_tablepre" size="30" disabled="true" style="background-color: #F5F5F5;"></td>
<td>主数据库表前缀</td>
</tr>
</table>
<input type="submit" value="提交" onclick="$('command').value='edit';">
<br><br>
<div style="color: #8B0000; font-size: 12px; text-align: center;">
注意:主从数据库配置不能通过此界面修改,请直接编辑 config.inc.php 文件或重新运行安装脚本。
</div>
</form>
\ No newline at end of file
......@@ -21,13 +21,69 @@
{template useradvdata}
</div>
</div>
<div id="postdata">
<input type="submit" id="post" onClick="postCmd('userdata','user.php');return false;" value="{lang submit}">
<input type="reset" id="reset" name="reset" value="{lang reset}">
</div>
</form>
<!--{if $show_sync_button}-->
<div style="margin-top: 20px; padding: 15px; border: 2px solid #FFD700; background-color: #FFFACD; border-radius: 5px;">
<div style="text-align: center; font-weight: bold; color: #B8860B; margin-bottom: 10px;">
主从数据库同步功能
</div>
<form method="post" action="user.php" name="syncdata" id="syncform">
<input type="hidden" name="mode" value="sync_master">
<table align="center" style="border-collapse: collapse;">
<tr>
<td style="padding: 5px; text-align: right;">主服务器用户名:</td>
<td style="padding: 5px;">
<input type="text" name="master_username" id="master_username" size="20" placeholder="在{$master_server_name}的用户名">
</td>
</tr>
<tr>
<td style="padding: 5px; text-align: right;">主服务器密码:</td>
<td style="padding: 5px;">
<input type="password" name="master_password" id="master_password" size="20" placeholder="在{$master_server_name}的密码">
</td>
</tr>
<tr>
<td colspan="2" style="padding: 10px; text-align: center;">
<input type="button" value="{$sync_button_text}" onclick="syncMasterData();" style="padding: 8px 15px; background-color: #FFD700; border: 1px solid #B8860B; border-radius: 3px; cursor: pointer;">
</td>
</tr>
</table>
</form>
<!--{if $user_sync_status}-->
<div style="margin-top: 10px; text-align: center; font-size: 12px; color: #666;">
已绑定主服务器账户:{$user_sync_status['master_username']} (上次同步:{$user_sync_status['sync_time_formatted']})
</div>
<!--{/if}-->
<div style="margin-top: 10px; text-align: center; font-size: 11px; color: #888;">
注意:同步将覆盖当前账户的积分、成就、头衔等数据。每个主服务器账户只能绑定一个从服务器账户。
</div>
</div>
<script type="text/javascript">
function syncMasterData() {
var username = document.getElementById('master_username').value;
var password = document.getElementById('master_password').value;
if(!username || !password) {
alert('请输入主服务器的用户名和密码');
return false;
}
if(!confirm('确定要从主服务器同步数据吗?这将覆盖当前账户的相关数据。')) {
return false;
}
postCmd('syncform', 'user.php');
return false;
}
</script>
<!--{/if}-->
</center>
<br />
{template footer}
\ No newline at end of file
{template footer}
\ No newline at end of file
<?php
/**
* 主从数据库同步功能测试脚本
* Master-Slave Database Sync Test Script
*/
define('CURSCRIPT', 'test_masterslave');
require './include/common.inc.php';
require './include/masterslave.func.php';
echo "<h2>主从数据库同步功能测试</h2>";
// 显示当前配置
echo "<h3>当前配置</h3>";
echo "从服务器级别 (slave_level): " . $slave_level . "<br>";
echo "主服务器名称 (master_server_name): " . $master_server_name . "<br>";
echo "主数据库服务器 (master_dbhost): " . $master_dbhost . "<br>";
echo "主数据库名 (master_dbname): " . $master_dbname . "<br>";
echo "主数据库表前缀 (master_tablepre): " . $master_tablepre . "<br>";
echo "<hr>";
// 测试连接主数据库
echo "<h3>测试主数据库连接</h3>";
if($slave_level > 0) {
$master_db = connect_master_db();
if($master_db) {
echo "<span style='color: green;'>✓ 主数据库连接成功</span><br>";
// 测试查询主数据库用户表
$result = $master_db->query("SELECT COUNT(*) as count FROM {$master_tablepre}users", 'SILENT');
if(!$master_db->error() && $master_db->num_rows($result)) {
$count_data = $master_db->fetch_array($result);
echo "主数据库用户数量: " . $count_data['count'] . "<br>";
} else {
echo "<span style='color: orange;'>⚠ 无法查询主数据库用户表</span><br>";
}
} else {
echo "<span style='color: red;'>✗ 主数据库连接失败</span><br>";
}
} else {
echo "<span style='color: blue;'>ℹ 当前为主服务器,无需连接主数据库</span><br>";
}
echo "<hr>";
// 测试同步表创建
echo "<h3>测试同步表</h3>";
create_sync_table_if_not_exists();
$result = $db->query("SHOW TABLES LIKE '{$gtablepre}user_sync'", 'SILENT');
if($db->num_rows($result)) {
echo "<span style='color: green;'>✓ 同步表存在</span><br>";
// 显示同步记录
$sync_result = $db->query("SELECT * FROM {$gtablepre}user_sync ORDER BY sync_time DESC LIMIT 5");
if($db->num_rows($sync_result)) {
echo "<h4>最近的同步记录:</h4>";
echo "<table border='1' cellpadding='5'>";
echo "<tr><th>目标用户名</th><th>主服务器用户名</th><th>同步时间</th></tr>";
while($sync_data = $db->fetch_array($sync_result)) {
echo "<tr>";
echo "<td>" . htmlspecialchars($sync_data['target_username']) . "</td>";
echo "<td>" . htmlspecialchars($sync_data['master_username']) . "</td>";
echo "<td>" . date('Y-m-d H:i:s', $sync_data['sync_time']) . "</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "暂无同步记录<br>";
}
} else {
echo "<span style='color: red;'>✗ 同步表不存在</span><br>";
}
echo "<hr>";
// 功能状态检查
echo "<h3>功能状态检查</h3>";
echo "是否需要自动同步: " . (should_auto_sync() ? "<span style='color: green;'>是</span>" : "<span style='color: gray;'>否</span>") . "<br>";
echo "是否直接使用主数据库: " . (should_use_master_db() ? "<span style='color: green;'>是</span>" : "<span style='color: gray;'>否</span>") . "<br>";
if($slave_level >= 1 && !empty($master_server_name)) {
echo "主从同步功能: <span style='color: green;'>已启用</span><br>";
} else {
echo "主从同步功能: <span style='color: gray;'>未启用</span><br>";
}
// 显示当前使用的数据库表前缀
echo "当前游戏表前缀 (gtablepre): " . $gtablepre . "<br>";
if($slave_level == 3) {
echo "<span style='color: blue;'>ℹ 当前直接使用主数据库</span><br>";
}
echo "<hr>";
// 测试表单(仅在从服务器模式下显示)
if($slave_level >= 1 && !empty($master_server_name)) {
echo "<h3>测试同步功能</h3>";
echo "<form method='post' action='test_masterslave.php'>";
echo "<table>";
echo "<tr><td>主服务器用户名:</td><td><input type='text' name='test_master_username' size='20'></td></tr>";
echo "<tr><td>主服务器密码:</td><td><input type='password' name='test_master_password' size='20'></td></tr>";
echo "<tr><td>目标用户名:</td><td><input type='text' name='test_target_username' size='20' placeholder='留空使用主服务器用户名'></td></tr>";
echo "<tr><td colspan='2'><input type='submit' name='test_sync' value='测试同步'></td></tr>";
echo "</table>";
echo "</form>";
// 处理测试同步
if(isset($_POST['test_sync'])) {
$test_master_username = $_POST['test_master_username'];
$test_master_password = $_POST['test_master_password'];
$test_target_username = $_POST['test_target_username'] ?: $test_master_username;
if($test_master_username && $test_master_password) {
echo "<h4>同步测试结果:</h4>";
$sync_result = sync_user_from_master($test_master_username, $test_master_password, $test_target_username);
if($sync_result['success']) {
echo "<span style='color: green;'>✓ " . htmlspecialchars($sync_result['message']) . "</span><br>";
} else {
echo "<span style='color: red;'>✗ " . htmlspecialchars($sync_result['message']) . "</span><br>";
}
} else {
echo "<span style='color: red;'>✗ 请输入用户名和密码</span><br>";
}
}
}
echo "<hr>";
echo "<p><a href='admin.php'>返回管理界面</a> | <a href='user.php'>用户资料</a> | <a href='index.php'>游戏首页</a></p>";
?>
......@@ -3,6 +3,7 @@
define('CURSCRIPT', 'user');
require './include/common.inc.php';
require './include/masterslave.func.php';
//require './include/user.func.php';
......@@ -15,7 +16,27 @@ if(!isset($mode)){
$mode = 'show';
}
if($mode == 'edit') {
if($mode == 'sync_master') {
// 处理主从数据同步
$gamedata=Array();$gamedata['innerHTML']['info'] = '';
if($slave_level >= 1 && !empty($master_server_name)) {
if(!empty($master_username) && !empty($master_password)) {
$sync_result = sync_user_from_master($master_username, md5($master_password), $cuser);
$gamedata['innerHTML']['info'] .= $sync_result['message'] . '<br>';
} else {
$gamedata['innerHTML']['info'] .= '请输入主服务器的用户名和密码<br>';
}
} else {
$gamedata['innerHTML']['info'] .= '当前服务器不是从服务器或未配置主服务器信息<br>';
}
ob_clean();
$jgamedata = compatible_json_encode($gamedata);
echo $jgamedata;
ob_end_flush();
} elseif($mode == 'edit') {
$gamedata=Array();$gamedata['innerHTML']['info'] = '';
if($opass && $npass && $rnpass){
$pass_right = true;
......@@ -107,6 +128,15 @@ if($mode == 'edit') {
$select_icon = $icon;
//这里假定player表里有usertitle字段而且储存方式是这样蛋疼的。具体程序虚子你写。
$utlist = get_utitlelist();//然后去接收用户传来的$
// 主从同步相关变量
$show_sync_button = ($slave_level >= 1 && !empty($master_server_name));
$user_sync_status = get_user_sync_status($cuser);
if($user_sync_status) {
$user_sync_status['sync_time_formatted'] = date('Y-m-d H:i:s', $user_sync_status['sync_time']);
}
$sync_button_text = $user_sync_status ? "从{$master_server_name}同步已绑定的账号数据" : "从{$master_server_name}迁移用户和成就数据";
include template('user');
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment