系统重装后卡密数据全部丢失
现象描述
某次服务器迁移后,重新安装杰奇CMS 2.3版本,后台卡密管理页面显示“暂无数据”,用户购买记录、充值记录、未激活卡密全部消失,但数据库文件中仍能查到相关表存在。
杰奇CMS卡密数据保留的完整解决方案,七年运维老手的实战笔记
原因分析
这是一个典型的“表结构覆盖”问题,杰奇CMS在安装过程中,install()函数默认会对jieqi_system_card、jieqi_system_cardlog等核心表执行DROP TABLE IF EXISTS操作,即使你保留原有数据库文件,安装程序依然会清空这些表,许多新手运维误以为“保留数据库文件夹”就能保住数据,实则忽略了安装脚本的初始化逻辑。
解决步骤
-
备份现有数据库
mysqldump -u root -p --all-databases > full_backup_$(date +%Y%m%d).sql
特别导出卡密相关表:
mysqldump -u root -p jieqi_cms jieqi_system_card jieqi_system_cardlog > card_tables.sql
-
修改安装脚本
编辑/install/index.php,找到:$sql = "DROP TABLE IF EXISTS `jieqi_system_card`";
将其注释或删除(约第85-120行之间),如果使用完整安装包,建议直接替换为:
// 保留卡密数据,跳过删除操作
-
使用“增量安装”模式
在安装第4步“数据库配置”时,选择“仅更新表结构”而非“全新安装”,但杰奇官方安装包不提供此选项,此时可以手动执行:ALTER TABLE jieqi_system_card ADD COLUMN (需要新增的字段定义);
但最佳方案是:绕过安装程序,手动恢复数据库。
-
终极方案:手动衔接
- 停止web服务器,删除
/install文件夹 - 直接复制之前的数据库配置文件
/config/db_config.php到新环境 - 执行:
mysql -u root -p jieqi_cms < card_tables.sql
- 修复表关联:
UPDATE jieqi_system_card SET card_uid = (SELECT uid FROM jieqi_system_users WHERE condition...)
- 停止web服务器,删除
卡密充值后用户余额未增加
现象描述
用户输入20位卡密,系统提示“充值成功”,但用户账户余额、积分均无变化,后台卡密记录显示“已使用”,但无对应的充值日志。
原因分析
这是杰奇CMS 2.4版本的一个经典bug:card_charge()函数在调用update_user_money()时,未正确传递$userid变量,当用户第一次充值且未创建jieqi_system_money记录时,函数直接返回true但未真正写入,根源在于card_charge.php第67行缺少global $jieqiUserset声明。
解决步骤
-
定位问题代码
编辑/modules/card/class/card_charge.php,搜索:function card_charge($uid, $cardno, $money)
在函数体开头添加:
global $jieqiUserset, $jieqiGroups;
-
修正充值逻辑
找到约第72行:$result = jieqi_usermoney_update($uid, $money, 'card');
替换为:
require_once JIEQI_ROOT_PATH.'/class/users.php'; $userHandler = JieqiUsersHandler::getInstance('user'); $result = $userHandler->updateMoney($uid, $money, 2, '卡密充值'); -
验证充值记录
执行SQL检查:SELECT * FROM jieqi_system_money WHERE uid = 目标用户ID ORDER BY id DESC LIMIT 3;
若为空,则手动插入测试记录:
INSERT INTO jieqi_system_money (uid, money, reason, addtime) VALUES (1, 10.00, 'test', UNIX_TIMESTAMP());
批量导入卡密时提示“数据格式错误”
现象描述
上传2000条卡密CSV文件,点击“导入”后报错“第127行格式异常”,实际检查该行数据完全符合16位数字+4位校验码的规则,反复尝试,每次报错行号都不同。
原因分析
这不是数据问题,而是PHP的memory_limit与max_execution_time限制,杰奇CMS的导入函数batch_import_card()会逐行读取并校验,但当文件较大时,脚本可能因内存不足中断,且失败后未清理临时变量,导致后续校验出现鬼畜错误。
解决步骤
-
提升PHP执行环境
编辑php.ini(通常在/usr/local/php/etc/):memory_limit = 256M max_execution_time = 600 upload_max_filesize = 50M post_max_size = 50M
重启PHP-FPM:
systemctl restart php-fpm -
分批导入+去BOM头
使用Linux命令处理CSV:sed -i 's/\r//' card_list.csv # 清除Windows换行符 awk 'NR%500==1{close(f);f="split_"int(NR/500)“.csv”}{print > f}' card_list.csv然后逐批上传,每批不超过500行。
-
修改导入函数
编辑/modules/card/admin/import_card.php,找到:set_time_limit(120);
改为:
set_time_limit(0);
并在循环内增加:
if ($i % 100 == 0) { gc_collect_cycles(); // 强制垃圾回收 } -
终极方案:直接SQL导入
如果数据量超过1万条,绕过PHP:LOAD DATA LOCAL INFILE '/var/www/card_final.csv' INTO TABLE jieqi_system_card FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 LINES (card_no, card_pwd, card_money, card_area);
注意卡密表字段顺序需与CSV完全匹配。
卡密激活页一直显示“加载中”
现象描述
用户访问/modules/card/activate.php,页面一直旋转加载,F12查看网络请求,API接口ajax_check_card.php返回状态码200但无响应内容,持续约30秒后超时。
原因分析
这是Ajax请求被服务器URL重写规则拦截,杰奇CMS的伪静态规则中,/ajax/*路径通常被匹配到article模块,导致ajax_check_card被错误转发,很多运维在配置NGINX rewrite时,没有对API路径做例外处理。
解决步骤
-
检查NGINX配置
查看/etc/nginx/conf.d/jieqi.conf,常见错误配置:location / { rewrite ^/(.*)$ /index.php?$1 last; }这会吞掉所有AJAX请求。
-
添加例外规则
在server块内添加:location /modules/card/ajax/ { try_files $uri $uri/ /modules/card/ajax_check_card.php?$args; }或者更通用的做法:
location ~* /(ajax|api|service)/ { try_files $uri $uri/ /index.php?$args; } -
检查Apache用户
如果使用Apache,编辑.htaccess:RewriteRule ^modules/card/ajax/(.*)$ /modules/card/ajax_check_card.php?$1 [L,QSA]
-
验证JS路径
检查activate.php模板中的JS路径:<script>var ajaxurl = "/modules/card/ajax_check_card.php";</script>
确保相对路径正确,有的模板可能误写为
/ajax_check_card.php。
数据库连接失败导致卡密表无法查询
现象描述
后台卡密管理页面显示“数据库连接错误”,错误日志logs/error.log中出现:
[SQL Error] : Table 'jieqi_cms.jieqi_system_card' doesn't exist
但通过phpMyAdmin查看,该表明明存在。
原因分析
这是杰奇CMS一个隐蔽的数据库前缀bug,当数据库表前缀不是默认的jieqi_时,getTable()函数可能错误地拼接出jieqi_jieqi_system_card这种双前缀表名,通常发生在迁移数据库后,未正确修改config/db_config.php中的table_prefix变量。
解决步骤
-
验证当前前缀
查看/config/db_config.php:define('JIEQI_TABLE_PREFIX', 'jieqi_');对比实际表名:
SHOW TABLES LIKE 'jieqi%';
如果实际表名是
mycms_system_card,则前缀应为mycms_。 -
全局搜索硬编码前缀
杰奇某些旧版模块存在硬编码表名,搜索:grep -rn "jieqi_system_card" /var/www/jieqi/modules/card/
将发现的硬编码
jieqi_system_card改为使用常量:JIEQI_TABLE_PREFIX.'system_card'
-
检查MySQL用户权限
执行:SHOW GRANTS FOR 'jieqi_user'@'localhost';
确保有
SELECT, INSERT, UPDATE, DELETE权限,常见错误是只给了SELECT权限。 -
重建表索引
如果表存在但查询失败,可能是索引损坏:REPAIR TABLE jieqi_system_card; OPTIMIZE TABLE jieqi_system_card;
卡密表数据膨胀导致后台卡死
现象描述
运营一年后,卡密表数据达到80万行,后台打开“卡密管理”页面需要30秒,点击“导出”直接502错误,服务器CPU飙升到100%。
原因分析
杰奇CMS默认的admin_card.php查询全表数据时使用了SELECT *且无分页限制,当数据量超过10万行时,PHP的fetch_all()会撑爆内存,且没有对expire_time字段加索引,每次查询都全表扫描。
解决步骤
-
紧急处理:查询限行
临时修改/modules/card/admin/admin_card.php,在SQL查询末尾添加:$sql .= " LIMIT 1000";
-
优化表结构
对关键字段加索引:ALTER TABLE jieqi_system_card ADD INDEX idx_card_status (card_status); ALTER TABLE jieqi_system_card ADD INDEX idx_card_expire (card_expire_date);
-
启用分页查询
修改admin_card.php,使用jieqi_page_break()函数:$page_handler = JieqiPageHandler::getInstance(); $page_handler->setPageTotal($totalcount); $sql = "SELECT * FROM " . JIEQI_TABLE_PREFIX . "system_card ORDER BY card_id DESC " . $page_handler->getPageSql();
-
历史数据归档
每月定时将过期卡密移入历史表:CREATE TABLE jieqi_system_card_history LIKE jieqi_system_card; INSERT INTO jieqi_system_card_history SELECT * FROM jieqi_system_card WHERE card_expire_date < UNIX_TIMESTAMP(); DELETE FROM jieqi_system_card WHERE card_expire_date < UNIX_TIMESTAMP();
在后台添加“归档管理”链接,仅查询历史表。
卡密生成页面乱码
现象描述
后台“生成卡密”页面,输入金额后点击“生成”,页面出现“�”乱码,生成的部分卡密数字也变成乱码,如“12345678”变成“1?4?8”。
原因分析
这是编码转换问题,杰奇CMS的卡密生成函数create_card_no()使用chr()函数拼接字符,当PHP环境为UTF-8而系统默认改为了GBK时,chr(rand(48,57))生成的数字没问题,但校验码算法使用了ord()和chr()组合,会导致非ASCII字符乱码。
解决步骤
-
统一文件编码
检查所有PHP文件编码:file -i /var/www/jieqi/modules/card/class/card_create.php
确保输出为
charset=utf-8,若不是则用iconv转换:iconv -f gbk -t utf-8 card_create.php > card_create_utf8.php
-
修改校验码算法
编辑card_create.php,找到:$card_pwd = chr(rand(65,90)).chr(rand(48,57)).chr(rand(97,122));
替换为纯数字算法:
$card_pwd = str_pad(mt_rand(0, 9999), 4, '0', STR_PAD_LEFT);
-
数据库表字段修复
检查表字符集:SHOW CREATE TABLE jieqi_system_card;
若显示
CHARSET=gbk,则转换:ALTER TABLE jieqi_system_card CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
采集卡密时提示“验证码错误”
现象描述
使用杰奇CMS自带采集功能从其他站点获取卡密,每次提交验证码都提示错误,即使手动输入正确的验证码也不行,F12查看发现验证码请求返回的captcha_code与实际图片不一致。
原因分析
这是Session冲突问题,杰奇CMS的采集模块在captcha.php中使用了session_start(),但采集请求通常是CURL发起,没有携带Cookie,导致新生成的Session与当前采集会话不一致,某些服务器配置了session.use_strict_mode,强制验证客户端Session ID。
解决步骤
-
关闭Strict模式
编辑php.ini:session.use_strict_mode = 0
-
手动传递Session ID
修改采集函数,在CURL请求中添加Cookie:$ch = curl_init(); curl_setopt($ch, CURLOPT_COOKIE, session_name().'='.session_id());
-
使用OCR替代验证码
如果目标站验证码复杂,安装Tesseract:apt-get install tesseract-ocr php-tesseract
在采集代码中集成:
$text = (new TesseractOCR($captcha_image))->run();
-
终极方案:模拟浏览器
使用Puppeteer或Selenium采集时,接管验证码处理:await page.type('#captcha_input', captchaText); // 手动输入后提交
卡密有效期设置后不生效
现象描述
在后台将卡密有效期设为“2024-12-31”,但用户使用后,系统提示“卡密已过期”,实际日期还差半个月,反复修改,甚至设为2099年仍然提示过期。
原因分析
这是一个时区Bug,杰奇CMS在card_valid()函数中使用了strtotime('2024-12-31'),但PHP默认时区与数据库时区不一致,当PHP时区为UTC而数据库时区为Asia/Shanghai时,时间比较会偏差8小时,更坑的是,卡密生成的expire_time字段存的是UNIX时间戳,而用户验证时又用了date()函数转换。
解决步骤
-
统一时区
在/config/system.php开头添加:date_default_timezone_set('Asia/Shanghai'); -
修改时间计算逻辑
编辑/modules/card/class/card_valid.php,找到:if($card_expire < time())
改为:
if($card_expire + 86400 < time()) // 加一天缓冲
-
数据库时间字段修正
检查已有记录:SELECT card_expire_date, FROM_UNIXTIME(card_expire_date) FROM jieqi_system_card WHERE card_id = 100;
如果显示时间差8小时,批量修正:
UPDATE jieqi_system_card SET card_expire_date = card_expire_date + 28800;
搬家后卡密统计数字对不上
现象描述
从A服务器迁移到B服务器后,后台显示“已生成卡密20000张”,但实际数据库中只查到18000条记录,少了2000条,且“已使用”统计数也不对,用户实际充值记录有5000条,但显示只有3000条。
原因分析
这是MyISAM表跨平台迁移的常见问题,MyISAM表的计数缓存(table_rows)与操作系统字序有关,当从大端序服务器迁移到小端序服务器时,SHOW TABLE STATUS中的Rows字段会显示错误的缓存值,真正的数据并没有丢失,只是统计接口读取了缓存而非实际计数。
解决步骤
-
刷新表统计
执行:ANALYZE TABLE jieqi_system_card;
然后重新查看:
SHOW TABLE STATUS LIKE 'jieqi_system_card';
-
修改统计代码
编辑/modules/card/admin/admin_card.php,找到:$totalcount = $jieqi_system_card_handler->getCount();
替换为强制计数:
$totalcount = $jieqi_system_card_handler->query('SELECT COUNT(*) FROM '.JIEQI_TABLE_PREFIX.'system_card'); -
重建表引擎
将MyISAM改为InnoDB,避免此类问题:ALTER TABLE jieqi_system_card ENGINE=InnoDB; ALTER TABLE jieqi_system_cardlog ENGINE=InnoDB;
-
使用redis缓存计数
在/etc/init.d/写定时任务,每天凌晨统计:mysql -e "SELECT COUNT(*) FROM jieqi_system_card" > /tmp/card_count.txt redis-cli SET card_count $(cat /tmp/card_count.txt)
后台读取redis值代替数据库查询。



发表评论