<?php
include(dirname(__DIR__) . '/includes/common.php');
if($islogin==1){}else exit("<script language='javascript'>window.location.href='./login.php';</script>");
$act=isset($_GET['act'])?daddslashes($_GET['act']):null;
@header('Content-Type: application/json; charset=UTF-8');
switch($act){
case 'to_all_time':
	$biz = isset($_REQUEST['biz']) ? daddslashes($_REQUEST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$rs = $DB->query("SELECT id,endtime_arr FROM `{$qq_table}`");
	while($res = $rs->fetch()){
		$time_arr = json_decode($res['endtime_arr'],true);
		if(is_array($time_arr) && count($time_arr) == 1 && (isset($time_arr['9999']) || array_key_exists('9999', $time_arr))) continue;
		$max_time = is_end3($res['endtime_arr'])[0];
		$newtime = $max_time < $date ? $date : $max_time;
		$newtime_json = json_encode(['9999'=>$newtime], JSON_UNESCAPED_UNICODE);
		$DB->exec("UPDATE {$qq_table} SET endtime_arr='".$newtime_json."' WHERE id='{$res['id']}'");
	}
	exit('{"code":1,"msg":"ok"}');
break;
case 'renew_auth':
	$qq = isset($_POST['qq']) ? daddslashes($_POST['qq']) : '';
	$key = isset($_POST['key']) ? daddslashes($_POST['key']) : '';
	$url = isset($_POST['url']) ? daddslashes($_POST['url']) : '';
	$email = isset($_POST['email']) ? daddslashes($_POST['email']) : '';
	
	if(!$qq || !$key || !$url) {
		exit('{"code":-1,"msg":"参数不完整"}');
	}
	
	// 调用公共函数获取API链接
	include_once ROOT . 'includes/functions.php';
	$apiUrl = get_renew_auth_api_url();
	
	// 构建请求参数
	$params = array(
		'appid' => 1,
		'qq' => $qq,
		'url' => $url,
		'email' => $email ?: $qq . '@qq.com',
		'key' => $key
	);
	
	// 发起请求
	$response = auth_curl($apiUrl . '?' . http_build_query($params));
	$data = json_decode($response, true);
	
	exit(json_encode($data));
break;
case 'getcount':
	$thtime=date("Y-m-d").' 00:00:00';
	$all_user=$DB->getColumn("SELECT count(*) FROM pre_user");
	$all_qq=$DB->getColumn("SELECT count(*) FROM pre_qq");
	$rmb=$DB->getColumn("SELECT sum(money) FROM `pre_pay` WHERE `status`='1' AND `addtime` > '$thtime'");
	$rmb = $rmb==''?'0':$rmb;
	$yttime=date('Y-m-d H:i:s',strtotime('-1 day',strtotime($thtime)));
	$qq=$DB->getColumn("SELECT count(*) FROM `pre_qq` WHERE to_days(addtime) = to_days(now())");
	//在线QQ
	$online=$DB->getColumn("SELECT count(*) FROM pre_qq WHERE login_zt='1'");
	//全部卡密
	$kmnum=$DB->getColumn("SELECT count(*) FROM pre_km");
	//累计充值
	$all_rmb=$DB->getColumn("SELECT round(sum(money),2) FROM pre_pay WHERE status='1'");
	//今日新增用户
	$user=$DB->getColumn("SELECT count(*) FROM pre_user WHERE to_days(addtime) = to_days(now())");
    $labels = array();
    $data_pay = array();
    $data_qq = array();
    for ($i = 0; $i < 7; $i++) {
        $thtime = date("Y-m-d", strtotime("-" . $i . " day")) . " 23:59:59";
        $yesterday_time = date("Y-m-d", strtotime("-" . $i . " day")) . " 00:00:00";
        $payCount = $DB->getColumn("SELECT round(sum(money),2) FROM pre_pay WHERE status='1' AND addtime<='" . $thtime . "' AND addtime>'" . $yesterday_time . "'");
        $qqCount = $DB->getColumn("SELECT count(*) FROM pre_qq WHERE addtime<='" . $thtime . "' AND addtime>'" . $yesterday_time . "'");
        $labels[] = date("m.d", strtotime("-" . $i . " day"));
        $data_pay[] = floatval($payCount);
        $data_qq[] = $qqCount;
    }
    $labels = array_reverse($labels); 
    $data_pay = array_reverse($data_pay);
    $data_qq = array_reverse($data_qq);
	$result=array(
		"code"=>0,
		"user"=>$user,
		"all_user"=>$all_user,
		"qq"=>$qq,
		"all_qq"=>$all_qq,
		"online"=>$online,
		"kmnum"=>$kmnum,
		"rmb"=>round(floatval($rmb),2),
		"all_rmb"=>$all_rmb,
		"labels"=>$labels,
		"chart_pay"=> ['data_pay'=>$data_pay],
		"chart_qq"=> ['data_qq'=>$data_qq]
	);
	exit(json_encode($result));
break;
case 'batch_del_frame_qq':
	$id=intval($_POST['apiid']);
	$qq_json = isset($_POST['qq']) ? $_POST['qq'] : '';
	$qq_list = json_decode($qq_json, true);
	
	if(!is_array($qq_list) || empty($qq_list))exit('{"code":-1,"msg":"请选择要删除的QQ"}');
	
	$login = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$id}' LIMIT 1");
	if(!$login)exit('{"code":-1,"msg":"框架数据不存在"}');
	
	$results = batch_delete_qq($login, $qq_list);
	
	$success_count = 0;
	$fail_count = 0;
	foreach($results as $result){
		$del_data = $result['data']['delete'] ?? null;
		if (is_array($del_data)) {
			foreach ($del_data as $qq => $item) {
				(isset($item['bool']) && $item['bool']) ? $success_count++ : $fail_count++;
			}
		} elseif (isset($result['retcode']) && $result['retcode'] == 200) {
			$success_count += count($qq_list);
		} else {
			$fail_count += count($qq_list);
		}
	}
	
	exit(json_encode([
		'code' => 1,
		'msg' => '批量删除完成',
		'success_count' => $success_count,
		'fail_count' => $fail_count
	]));
break;
case 'batch_del_all_offline':
	$rs = $DB->query("SELECT * FROM `pre_loginconfig` WHERE status='1' ORDER BY id ASC");
	$logins = [];
	while ($row = $rs->fetch()) {
		$logins[] = $row;
	}
	if (empty($logins)) {
		exit(json_encode(['code' => -1, 'msg' => '没有可用的节点']));
	}
	$ret = batch_del_offline_all_nodes($logins);
	exit(json_encode([
		'code' => 1,
		'msg' => '一键删除完成',
		'success_count' => $ret['success_count'],
		'fail_count' => $ret['fail_count'],
		'node_results' => $ret['node_results']
	]));
break;
case 'get_frame_qq':
	$id=intval($_POST['apiid']);
	$login = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$id}' LIMIT 1");
	if(!$login)exit('{"code":1,"msg":"框架数据不存在"}');
	$arr = get_frame_qq($login);
	if(!isset($arr['retcode'])) $arr['retcode'] = -1;
	if($arr['retcode'] != 200)exit(json_encode(array('total'=>0,'rows'=>[])));
	$bot_data = $arr['bot'] ?? [];
	$list = $bot_data['list'] ?? [];
	$total = $bot_data['total'] ?? 0;
	$online_uins = [];
	foreach ($bot_data['online_list'] ?? [] as $r) {
		$qq = $r['bot'] ?? $r['uin'] ?? 0;
		if ($qq) $online_uins[$qq] = 1;
	}
	$rows = [];
	foreach ($list as $row) {
		$qq = $row['bot'] ?? $row['uin'] ?? 0;
		if (!$qq) continue;
		$online = ($row['online'] === true || $row['online'] === 'true' || ($row['status'] ?? null) == 1 || ($row['status'] ?? null) === '1')
			? true : (isset($online_uins[$qq]) ? true : false);
		$rows[] = [
			'qq' => $qq,
			'zt' => $online ? '登录成功' : '未登录',
			'nick' => $row['nick'] ?? ''
		];
	}
	exit(json_encode(array('total'=>$total,'rows'=>$rows)));
break;
case 'shopagent':
	$zid = intval($_POST['zid']);
	$row = $DB->getRow("SELECT upzid_all FROM pre_user WHERE zid='{$zid}' LIMIT 1");
	if(!$row || empty($row['upzid_all'])){
		exit('当前用户没有上级');
	}
	$zidarr = explode('_',$row['upzid_all']);
	// 过滤空值和非数字
	$zidarr = array_filter($zidarr, function($value) {
		return is_numeric($value) && $value > 0;
	});
	if(empty($zidarr)){
		exit('当前用户没有上级');
	}
	$rs=$DB->query("SELECT zid,user FROM pre_user WHERE zid in(".implode(',',$zidarr).") ORDER BY zid DESC");
	if(!$rs){
		exit('查询上级失败');
	}
	$ret[] = '当前用户的上级有<br>';
	while($res = $rs->fetch()){
		$ret[] = 'ZID：'.$res['zid'].'----用户名：'.$res['user'];
	}
	exit(implode('<br>',$ret));
break;
case 'kami_qk':
	$type = intval($_POST['kami_qk_type']);
	if($type == 0){//清空-不退回余额
		$DB->exec("TRUNCATE TABLE `pre_km`");
	}elseif($type == 1){//清空-并退回余额
		$tmp_user = array();
		$rs=$DB->query("SELECT * FROM pre_km");
		while($res = $rs->fetch()){
			if($res['zid'] == 1){
				$DB->exec("DELETE FROM `pre_km` WHERE id='{$res['id']}'");
				continue;
			}
			if(!isset($tmp_user[$res['zid']])){
				$tmp_user[$res['zid']] = $DB->getRow("SELECT * FROM `pre_user` WHERE zid ='{$res['zid']}'");
			}
			$q_rmb = $tmp_user[$res['zid']]['rmb'];
			$tmp_zid = $tmp_user[$res['zid']]['zid'];
			if($DB->exec("DELETE FROM `pre_km` WHERE id='{$res['id']}'") && $res['isuse'] == '0'){
				$DB->exec("UPDATE `pre_user` SET `rmb`=`rmb`+'{$res['rmb']}' WHERE zid='{$tmp_zid}'");
				add_money_log($tmp_zid,'余额','退款',$res['rmb'],$q_rmb,($q_rmb+$res['rmb']),'管理员删除卡密:'.$res['km'].'退回'.$res['rmb'].'元');
				$q_rmb = $q_rmb+$res['rmb'];
				$tmp_user[$res['zid']]['rmb'] = $q_rmb;
			}
		}
	}elseif($type == 99){//清空已使用
		$DB->exec("DELETE FROM `pre_km` WHERE isuse='1'");
	}
	exit('{"code":1,"msg":"操作成功"}');
break;
case 'get_WxPusher_qr':
	$wechat_apptoken = daddslashes($_POST['wechat_apptoken']);
	if($wechat_apptoken == '')exit('{"code":-1,"msg":"apptoken不能为空"}');
	$post = '{"appToken":"'.$wechat_apptoken.'","extra":"'.time().'","validTime":1800}';
	$ret = get_curl2('https://wxpusher.zjiecode.com/api/fun/create/qrcode',$post,0,['Content-Type: application/json']);
	exit($ret);
break;
case 'add_qq_time':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$cid = intval($_POST['cid']);
	$qq = daddslashes($_POST['qq']);
	$type = intval($_POST['type']);
	$num = intval($_POST['num']);
	$userzid = intval($_POST['userzid']);
	$row = $DB->getRow("SELECT * FROM `{$qq_table}` WHERE qq='{$qq}' LIMIT 1");
	$goods = array(
		'type'=>$cid,
		'num'=>$num,
		'time_type'=>$type
	);
	if($row){
		$t = edit_endtime($goods,$row['endtime_arr'],'+');
		$sql = "UPDATE {$qq_table} SET endtime_arr='{$t}' WHERE qq='{$qq}'";
	}else{//新增
		$tmp = $DB->getRow("SELECT zid FROM `pre_user` WHERE zid='{$userzid}' LIMIT 1");
		if(!$tmp)exit('{"code":-1,"msg":"zid用户不存在"}');
		$t = edit_endtime($goods,'{}','+');
		$dropline = intval($conf['dropline_user']);
		$sql = "INSERT INTO `{$qq_table}` (`zid`,`qq`,`dropline`,`addtime`,`endtime_arr`) VALUES ('".$userzid."', '".$qq."', '".$dropline."', NOW(), '".$t."')";
	}
	if($DB->exec($sql)){
		exit('{"code":1,"msg":"操作成功"}');
	}else{
		exit('{"code":-1,"msg":"操作失败"}');
	}
break;
case 'all_del_qq'://一键删除所有过期的分类和过期QQ：先处理全部分类过期的（删QQ），再处理单个/多个分类过期的（只删到期时间）
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$type = intval($_POST['type']);
	global $date;
	$rs = $DB->query("SELECT * FROM pre_loginconfig WHERE biz='{$biz}'");
	$login = [];
	while($res = $rs->fetch()){
		$login[$res['id']] = $res;
	}
	$deleted_qq_count = 0;
	$qq_to_del = [];
	$login_map = [];
	// 第一轮：全部分类过期的 -> 删除QQ（网站端+可选框架）
	$rs = $DB->query("SELECT id,qq,cityid,endtime_arr FROM {$qq_table}");
	while($res = $rs->fetch()){
		$endtime_arr = json_decode($res['endtime_arr'], true);
		if(!is_array($endtime_arr)) $endtime_arr = [];
		$all_expired = true;
		foreach($endtime_arr as $time){
			if($time > $date){ $all_expired = false; break; }
		}
		if($all_expired && count($endtime_arr) > 0){
			$deleted_qq_count++;
			if($biz === 'cloud' && $type == 1 && isset($login[$res['cityid']])){
				$qq_to_del[] = $res['qq'];
				$login_map[$res['cityid']][] = $res['qq'];
			}
			if($biz === 'cloud'){
				set_qq_user($res['qq'], 0, 1);
			}else{
				$DB->exec("DELETE FROM {$qq_table} WHERE id='".intval($res['id'])."'");
			}
		}
	}
	if($biz === 'cloud' && count($qq_to_del) > 0){
		foreach($login_map as $cityid => $login_qqs) {
			if(isset($login[$cityid]) && ($login[$cityid]['type'] != 'myqq' && $login[$cityid]['type'] != 'myqqa')){
				batch_delete_qq($login[$cityid], $login_qqs, 10);
			}
		}
	}
	// 第二轮：单个或多个分类过期的 -> 只删除过期分类的到期时间（更新 endtime_arr）
	$cleaned_count = 0;
	$rs = $DB->query("SELECT id,endtime_arr FROM {$qq_table}");
	while($res = $rs->fetch()){
		$endtime_arr = json_decode($res['endtime_arr'], true);
		if(!is_array($endtime_arr)) $endtime_arr = [];
		$valid = [];
		foreach($endtime_arr as $cid => $time){
			if($time > $date) $valid[$cid] = $time;
			else $cleaned_count++;
		}
		if(count($valid) < count($endtime_arr) && count($valid) > 0){
			$new_json = addslashes(json_encode($valid));
			$DB->exec("UPDATE {$qq_table} SET endtime_arr='{$new_json}' WHERE id='{$res['id']}'");
		}
	}
	$msg = '';
	if($deleted_qq_count > 0) $msg .= '删除全部分类过期的QQ '.$deleted_qq_count.' 个';
	if($cleaned_count > 0) $msg .= ($msg ? '；' : '') . '清理过期分类 '.$cleaned_count.' 项';
	if($msg === '') $msg = '暂无过期数据';
	exit('{"code":1,"msg":"'.$msg.'"}');
break;
case 'all_edit_time'://一键增减时长
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$cid = intval($_POST['cid']);
	$num = intval($_POST['num']);
	if($num==0)exit('{"code":-1,"msg":"参数不正确"}');
	$remark = daddslashes($_POST['remark']);
	if(intval($_POST['renew_type'])==0){
		$renew_type = '+';
		$log = '增加时长';
	}else{
		$renew_type = '-';
		$log = '减少时长';
	}
	$tmp = array(
		'type'=>$cid,
		'num'=>$num,
		'time_type'=>intval($_POST['type'])//0天-1月
	);
	$i = 1;
	$count = $DB->getColumn("SELECT count(*) FROM {$qq_table}");
	$up_num = 50;
	$id_arr = array();
	$end_arr = array();
	$value = array();
	$rs=$DB->query("SELECT id,zid,qq,endtime_arr FROM {$qq_table}");
	while($res = $rs->fetch()){
		$t = edit_endtime($tmp,$res['endtime_arr'],$renew_type);
		$id_arr[] = $res['id'];
		$end_arr[] = "WHEN ".$res['id']." THEN '".$t."'";
		$end = json_decode($res['endtime_arr'],true);
		if(isset($end[$cid])){
			$yuan = $end[$cid];
		}else{
			$yuan = $date;
		}
		$end2 = json_decode($t,true);
		$xian = $end2[$cid];
		$value[] = "('{$res['zid']}','{$log}','qq={$res['qq']}，原{$yuan}，现{$xian}".($remark==''?'':'，'.$remark)."',NOW())";
		if($i % $up_num == 0 || $i == $count){
			$sql = "UPDATE {$qq_table} SET endtime_arr = CASE id ".implode(' ',$end_arr)." END WHERE id IN (".implode(',',$id_arr).")";
			$DB->exec($sql);
			$sql = "INSERT INTO `pre_points` (`zid`,`type`,`bz`,`addtime`) VALUES ".implode(',',$value);
			$DB->exec($sql);
			//重置
			$id_arr = [];
			$end_arr = [];
			$value = [];
		}
		$i++;
	}
	exit('{"code":1,"msg":"已操作"}');
break;
case 'time_change'://分类时长处理
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$apiid = intval($_POST['apiid']);//0默认-1独享-2独享-默认
	$cids = intval($_POST['cids']);
	$rs = $DB->query("SELECT * FROM `{$qq_table}`");
	while($res = $rs->fetch()){
		$time_arr = json_decode($res['endtime_arr'],true);
		if(!$time_arr)$time_arr['endtime'] = $date;
		if(isset($time_arr['free']))unset($time_arr['free']);
		if($apiid == 0 && array_key_exists('endtime',$time_arr)){
			$k2 = strval($cids);
			if(array_key_exists($k2,$time_arr)){
				if($time_arr['endtime']>$time_arr[$k2]){
					$time_arr[$k2] = $time_arr['endtime'];
				}
			}else{
				$time_arr[$k2] = $time_arr['endtime'];
			}
			unset($time_arr['endtime']);
			$t = json_encode($time_arr);
			$DB->exec("UPDATE `{$qq_table}` SET endtime_arr='".$t."' WHERE id='{$res['id']}'");
		}else{
			$k = strval($apiid);
			$k2 = strval($cids);
			if(array_key_exists($k,$time_arr)){
				$t_arr = [];
				foreach (array_keys($time_arr[$k]) as $key){
					$t_arr[] = $time_arr[$k][$key];
				}
				usort($t_arr, "compareByTimeStamp");
				$tmp = isset($t_arr[0])?$t_arr[0]:$date;
				if(array_key_exists($k2,$time_arr)){
					if($tmp>$time_arr[$k2]){
						$time_arr[$k2] = $tmp;
					}
				}else{
					$time_arr[$k2] = $tmp;
				}
				unset($time_arr[$k]);
				$t = json_encode($time_arr);
				$DB->exec("UPDATE `{$qq_table}` SET endtime_arr='{$t}' WHERE id='{$res['id']}'");
			}
		}
	}
	exit('{"code":1,"msg":"已转换"}');
break;
case 'transfer_login_class_biz':
	$src_biz = isset($_POST['src_biz']) ? daddslashes($_POST['src_biz']) : '';
	$dst_biz = isset($_POST['dst_biz']) ? daddslashes($_POST['dst_biz']) : '';
	$src_biz = $src_biz === 'dg' ? 'dg' : 'cloud';
	$dst_biz = $dst_biz === 'dg' ? 'dg' : 'cloud';
	if($src_biz === $dst_biz) exit(json_encode(['code' => -1, 'msg' => '目标版本不能相同']));
	$src_cid = intval($_POST['src_cid'] ?? 0);
	if($src_cid <= 0) exit(json_encode(['code' => -1, 'msg' => '参数错误']));

	$srcClass = $DB->getRow("SELECT id,name FROM pre_login_class WHERE id='{$src_cid}' AND biz='{$src_biz}' LIMIT 1");
	if(!$srcClass) exit(json_encode(['code' => -1, 'msg' => '源分类不存在']));

	$srcQQTable = $src_biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$dstQQTable = $dst_biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';

	$txOk = false;
	if(method_exists($DB, 'beginTransaction')){
		$txOk = $DB->beginTransaction();
	}

	$classMoved = $DB->exec("UPDATE pre_login_class SET biz='{$dst_biz}' WHERE id='{$src_cid}' AND biz='{$src_biz}'");
	if($classMoved === false || intval($classMoved) <= 0){
		if($txOk && method_exists($DB, 'rollBack')) $DB->rollBack();
		exit(json_encode(['code' => -1, 'msg' => '转移失败：分类更新失败']));
	}

	$nodeMoved = $DB->exec("UPDATE pre_loginconfig SET biz='{$dst_biz}' WHERE biz='{$src_biz}' AND cids='{$src_cid}'");
	if($nodeMoved === false){
		if($txOk && method_exists($DB, 'rollBack')) $DB->rollBack();
		exit(json_encode(['code' => -1, 'msg' => '转移失败：接口更新失败']));
	}

	$goodsAffected = 0;
	if($src_biz === 'cloud'){
		$goodsSql = "UPDATE pre_goods SET version=(CASE WHEN version='cloud_all' THEN 'dg_all' ELSE 'dg' END) WHERE type='{$src_cid}' AND (version='cloud' OR version='' OR version IS NULL OR version='cloud_all' OR version='all')";
		$goodsExec = $DB->exec($goodsSql);
		$goodsAffected = $goodsExec === false ? 0 : intval($goodsExec);
	}elseif($src_biz === 'dg'){
		$goodsSql = "UPDATE pre_goods SET version=(CASE WHEN version='dg_all' THEN 'cloud_all' ELSE 'cloud' END) WHERE type='{$src_cid}' AND (version='dg' OR version='dg_all')";
		$goodsExec = $DB->exec($goodsSql);
		$goodsAffected = $goodsExec === false ? 0 : intval($goodsExec);
	}

	$qqMoved = 0;
	$qqUpdated = 0;
	$qqDeleted = 0;
	$qqErrors = 0;
	$kw = "'%\"" . strval($src_cid) . "\":%'";
	$rs = $DB->query("SELECT * FROM {$srcQQTable} WHERE endtime_arr LIKE ".$kw);
	while($row = $rs->fetch(PDO::FETCH_ASSOC)){
		$endRaw = (string)($row['endtime_arr'] ?? '');
		$end = json_decode($endRaw, true);
		if(!is_array($end)) continue;
		$srcKey = strval($src_cid);
		if(!array_key_exists($srcKey, $end)) continue;
		$expire = $end[$srcKey];
		unset($end[$srcKey]);

		$zid = intval($row['zid'] ?? 0);
		$qqRaw = (string)($row['qq'] ?? '');
		if($qqRaw === '' || $zid <= 0){
			$qqErrors++;
			continue;
		}
		$qqDb = daddslashes($qqRaw);

		$dstRow = $DB->getRow("SELECT id,endtime_arr FROM {$dstQQTable} WHERE qq='{$qqDb}' AND zid='{$zid}' LIMIT 1");
		$dstKey = $srcKey;
		if($dstRow){
			$dstEndRaw = (string)($dstRow['endtime_arr'] ?? '');
			$dstEnd = json_decode($dstEndRaw, true);
			if(!is_array($dstEnd)) $dstEnd = [];
			if(isset($dstEnd[$dstKey])){
				$a = $dstEnd[$dstKey];
				$b = $expire;
				if(is_numeric($a) && is_numeric($b)){
					$dstEnd[$dstKey] = max(intval($a), intval($b));
				}else{
					$dstEnd[$dstKey] = ((string)$b > (string)$a) ? $b : $a;
				}
			}else{
				$dstEnd[$dstKey] = $expire;
			}
			$newDstEndDb = addslashes(json_encode($dstEnd, JSON_UNESCAPED_UNICODE));
			$DB->exec("UPDATE {$dstQQTable} SET endtime_arr='{$newDstEndDb}' WHERE id='".intval($dstRow['id'])."'");
		}else{
			$pwdDb = daddslashes((string)($row['pwd'] ?? ''));
			$cityid = intval($row['cityid'] ?? 0);
			$newCityid = $cityid;
			$online = intval($row['online'] ?? 0);
			$dropline = intval($row['dropline'] ?? 0);
			$protocolDb = daddslashes((string)($row['protocol'] ?? ''));
			$loginZt = intval($row['login_zt'] ?? 0);
			$loginCount = intval($row['login_cuont'] ?? 0);
			$uptimeDb = daddslashes((string)($row['uptime'] ?? ''));
			$addtimeDb = daddslashes((string)($row['addtime'] ?? ''));
			$droplineTime = intval($row['dropline_time'] ?? 0);
			$outNotifyDb = daddslashes((string)($row['out_notify'] ?? ''));
			$newDstEndDb = addslashes(json_encode([$dstKey => $expire], JSON_UNESCAPED_UNICODE));

			$sql = "INSERT INTO {$dstQQTable} (`zid`,`qq`,`pwd`,`cityid`,`online`,`dropline`,`protocol`,`login_zt`,`login_cuont`,`uptime`,`addtime`,`endtime_arr`,`dropline_time`,`out_notify`) VALUES ('{$zid}','{$qqDb}','{$pwdDb}','{$newCityid}','{$online}','{$dropline}','{$protocolDb}','{$loginZt}','{$loginCount}','{$uptimeDb}','{$addtimeDb}','{$newDstEndDb}','{$droplineTime}','{$outNotifyDb}')";
			$ok = $DB->exec($sql);
			if($ok === false){
				$qqErrors++;
			}else{
				$qqMoved++;
			}
		}

		$hasNumeric = false;
		foreach($end as $k => $v){
			$ks = (string)$k;
			if($ks !== '' && ctype_digit($ks)){
				$hasNumeric = true;
				break;
			}
		}
		if(!$hasNumeric){
			$DB->exec("DELETE FROM {$srcQQTable} WHERE id='".intval($row['id'])."'");
			$qqDeleted++;
		}else{
			$newSrcEndDb = addslashes(json_encode($end, JSON_UNESCAPED_UNICODE));
			$DB->exec("UPDATE {$srcQQTable} SET endtime_arr='{$newSrcEndDb}' WHERE id='".intval($row['id'])."'");
			$qqUpdated++;
		}
	}

	if($txOk && method_exists($DB, 'commit')){
		$DB->commit();
	}

	$msg = "转移完成：接口更新{$nodeMoved}个，商品更新{$goodsAffected}个，QQ新增{$qqMoved}个、原表更新{$qqUpdated}个、原表删除{$qqDeleted}个";
	if($qqErrors > 0) $msg .= "，异常{$qqErrors}个";
	exit(json_encode(['code' => 1, 'msg' => $msg]));
break;
case 'edit_login_class_id':
	$id = intval($_POST['id']);
	$cids = intval($_POST['cids']);
	$type = daddslashes($_POST['type']);
	if($type == 'hktype'){
		$sql = "UPDATE pre_loginconfig SET hktype='{$cids}' WHERE id='{$id}'";
	}else{
		$sql = "UPDATE pre_loginconfig SET cids='{$cids}' WHERE id='{$id}'";
	}
	if($DB->exec($sql)){
		exit('{"code":1,"msg":"修改成功"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}
break;
case 'edit_login_class_sort':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	foreach($_POST['sort'] as $id=>$sort){
		$DB->exec("UPDATE `pre_login_class` SET sort='$sort' WHERE id='{$id}' AND biz='{$biz}'");
	}
	exit('{"code":1,"msg":"排序成功！"}');
break;
case 'login_class_sort':
	$biz = isset($_GET['biz']) ? daddslashes($_GET['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$data=$DB->getAll("SELECT * FROM pre_login_class WHERE biz='{$biz}' ORDER BY sort ASC");
	exit(json_encode($data));
break;
case 'edit_login_class':
	$id = intval($_POST['id']);
	$name = daddslashes($_POST['name']);
	$type = daddslashes($_POST['type']);
	$free = intval($_POST['free']);
	$zt = intval($_POST['zt']);
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	if($type == 'set'){
		// 检查值是否有变化
		$current = $DB->getRow("SELECT name, free FROM pre_login_class WHERE id='{$id}' AND biz='{$biz}' LIMIT 1");
		if($current['name'] == $name && $current['free'] == $free){
			exit('{"code":1,"msg":"未更改"}');
		}
		$sql = "UPDATE pre_login_class SET name='{$name}',free='{$free}' WHERE id='{$id}' AND biz='{$biz}'";
	}elseif($type == 'del'){
		if($id == 1000)exit('{"code":-1,"msg":"默认分类 不能删除"}');
		$kw = "'%\"" . strval($id) . "\":%'";
		$num = $DB->getColumn("SELECT count(*) FROM {$qq_table} WHERE endtime_arr LIKE ".$kw);
		if($num>0)exit('{"code":-2,"msg":"该分类有 '.$num.' 个QQ用户在使用","num":'.$num.',"id":'.$id.'}');
		$sql = "DELETE FROM pre_login_class WHERE id='{$id}' AND biz='{$biz}'";
	}elseif($type == 'status'){
		// 检查状态是否有变化
		$current = $DB->getRow("SELECT active FROM pre_login_class WHERE id='{$id}' AND biz='{$biz}' LIMIT 1");
		if($current['active'] == $zt){
			exit('{"code":1,"msg":"未更改"}');
		}
		$sql = "UPDATE pre_login_class SET active='{$zt}' WHERE id='{$id}' AND biz='{$biz}'";
	}
	$result = $DB->exec($sql);
	if($result !== false){
		exit('{"code":1,"msg":"操作成功"}');
	}else{
		exit('{"code":-1,"msg":"操作失败'.$DB->error().'"}');
	}
break;
case 'del_login_class_force':
	$id = intval($_POST['id']);
	if($id == 1000)exit('{"code":-1,"msg":"默认分类 不能删除"}');
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';

	$kw = "'%\"" . strval($id) . "\":%'";
	// 获取所有使用该分类的QQ
	$rs = $DB->query("SELECT id, endtime_arr FROM {$qq_table} WHERE endtime_arr LIKE ".$kw);
	$success_count = 0;
	while($row = $rs->fetch(PDO::FETCH_ASSOC)){
		$endtime_arr = json_decode($row['endtime_arr'], true);
		if(isset($endtime_arr[$id])){
			unset($endtime_arr[$id]);
			$new_endtime_arr = json_encode($endtime_arr);
			$DB->exec("UPDATE {$qq_table} SET endtime_arr='".$new_endtime_arr."' WHERE id='".$row['id']."'");
			$success_count++;
		}
	}

	// 删除分类
	$result = $DB->exec("DELETE FROM pre_login_class WHERE id='{$id}' AND biz='{$biz}'");
	if($result !== false){
		exit('{"code":1,"msg":"已删除分类，并清除了 '.$success_count.' 个QQ的该分类到期时间"}');
	}else{
		exit('{"code":-1,"msg":"删除失败'.$DB->error().'"}');
	}
break;
case 'add_login_class':
	$name = daddslashes($_POST['name']);
	if($name=='')exit('{"code":-1,"msg":"分类名称不能为空"}');
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$sort = $DB->getRow("SELECT sort FROM `pre_login_class` ORDER BY sort DESC LIMIT 1");
	$sort = intval($sort['sort'])+1;
	if($DB->exec("INSERT INTO pre_login_class (`name`,`sort`,`biz`) VALUES('{$name}','{$sort}','{$biz}')")){
		exit('{"code":1,"msg":"添加成功"}');
	}else{
		exit('{"code":-1,"msg":"添加失败'.$DB->error().'"}');
	}
break;
case 'get_login_class':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$offset=$pagesize*($page - 1);
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	if(!in_array($biz, ['all','cloud','dg'], true)) $biz = 'cloud';
	$where = $biz === 'all' ? "(biz='cloud' OR biz='dg')" : "biz='{$biz}'";
	$data=$DB->getAll("SELECT * FROM pre_login_class WHERE {$where} ORDER BY id DESC LIMIT $offset,$pagesize");
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_login_class WHERE {$where}");
		$pages = ceil($total/$pagesize);	//总页数
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data)));
break;
case 'del_p'://删除协议id
    $id = intval($_POST['id']);
    $DB->exec("DELETE FROM pre_protocol WHERE id='{$id}'");
    exit('{"code":1,"msg":"已删除"}');
break;
case 'custom_protocol':
    $name = daddslashes($_POST['protocol_name']);
    $pid = $_POST['pid'];
    $namearr = $_POST['p_name'];
    $i=0;
    $sort = $DB->getColumn("SELECT sort FROM pre_protocol ORDER BY sort DESC LIMIT 1");
    foreach ($namearr as $v) {
    	$sort++;
    	$sql = "INSERT INTO `pre_protocol` (`sort`,`cid`,`name`,`type`) values ('".$sort."','".$name."','".$v."','".$pid[$i]."')";
    	$DB->exec($sql);
    	$i++;
    }
    exit('{"code":1,"msg":"添加成功"}');
break;
case 'batch_qq':
    $id = intval($_POST['id']);
    $num = intval($_POST['num']);
    if($num<0)exit('{"code":-1,"msg":"协议数量要大于1"}');
    $login = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$id}' LIMIT 1");
    $data = [];
    $pid= [];
    for ($i=0; $i < $num; $i++) {
        $si = strval($i);
        $qq = '123456'.$si;
        $ret = addqq($login,$qq,$qq,$si);
        
        // 检查是否有错误信息
        if (isset($ret['code']) && $ret['code'] == -1) {
            // 遇到错误，立即停止并返回错误信息
            exit('{"code":-1,"msg":"'.($ret['msg'] ?? '登录接口连接失败，请检查框架地址和端口是否正确').'"}');
        }
        
        // 检查返回值，支持不同格式的响应
        $success = false;
        if (isset($ret['code']) && $ret['code'] == 1) {
            $success = true;
        } elseif (isset($ret['bool']) && $ret['bool'] == true) {
            $success = true;
        } elseif (isset($ret['retcode']) && $ret['retcode'] == 200) {
            $success = true;
        }
        
        if($success){
            $pid[] = $si;
            $data[] = '测试号='.$qq.'---协议ID='.$si.'---添加成功';
        }else{
            $data[] = '测试号='.$qq.'---协议ID='.$si.'---<font>添加失败</font>';
        }
        usleep(150000);
    }
    exit('{"code":1,"id":"'.implode('-',$pid).'","msg":"'.implode('<br>',$data).'"}');
break;
case 'check_api':
	$url = daddslashes($_POST['url']);
	if(!$url) exit(json_encode(['code'=>-1,'msg'=>'url empty']));
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_HEADER, false);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
	curl_setopt($ch, CURLOPT_TIMEOUT, 5);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	$resp = curl_exec($ch);
	$err = curl_error($ch);
	$http = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
	$t = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
	curl_close($ch);
	if($resp === false && $http === 0){
		exit(json_encode(['code'=>-1,'msg'=>$err?:'request failed']));
	}
	exit(json_encode(['code'=>1,'msg'=>'ok','http'=>$http,'time'=>$t]));
break;
case 'add_user':
    $user=daddslashes($_POST['user']);
    $pwd=daddslashes($_POST['pwd']);
    $qq=daddslashes($_POST['qq']);
    if(!preg_match('/^[a-zA-Z0-9\x7f-\xff]+$/',$user)) {
        exit('{"code":-1,"msg":"用户名只能为英文、数字与汉字！"}');
    } elseif ($DB->getRow("SELECT zid FROM pre_user WHERE user=:user LIMIT 1", [':user'=>$user])) {
        exit('{"code":-1,"msg":"用户名已存在！"}');
    } elseif (strlen($pwd) < 6) {
        exit('{"code":-1,"msg":"密码不能低于6位"}');
    } elseif (strlen($qq) < 4 || !preg_match('/^[0-9]+$/',$qq)) {
        exit('{"code":-1,"msg":"QQ格式不正确！"}');
    } elseif ($pwd == $user) {
        exit('{"code":-1,"msg":"用户名和密码不能相同！"}');
    }
    $upzid_all = '';
    $user_vip = intval($conf['user_vip']);
    if($user_vip >= 1 && $user_vip <=5){
    }else{
        $user_vip = '1';
    }
    $sql = "INSERT INTO `pre_user` (`upzid`,`upzid_all`,`user`,`pwd`,`qq`,`vip`,`addtime`) values ('1','".$upzid_all."','".$user."','".$pwd."','".$qq."','".$user_vip."',NOW())";
    if($DB->exec($sql)){
        exit('{"code":1,"user":"'.$user.'","msg":"添加成功"}');
    }else{
        exit('{"code":-1,"msg":"添加失败，'.$DB->error().'"}');
    }
break;
case 'setapiid':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$id = intval($_POST['id']);
	$apiid = intval($_POST['apiid']);
	$DB->exec("UPDATE `{$qq_table}` SET cityid='{$apiid}' WHERE id='{$id}'");
	exit('{"code":1,"msg":"操作成功！"}');
break;
case 'getapiid':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$arr = $DB->getAll("SELECT * FROM pre_loginconfig WHERE biz='{$biz}' ORDER BY sort ASC,id ASC");
	exit(json_encode($arr));
break;
case 'setzid':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$id = intval($_POST['id']);
	$zid = intval($_POST['zid']);
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"用户数据不存在"}');
	$qqrow = $DB->getRow("SELECT qq FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
	if(!$qqrow)exit('{"code":-1,"msg":"数据不存在"}');
	if($biz === 'cloud'){
		set_qq_user($qqrow['qq'],$row['zid']);
	}else{
		$DB->exec("UPDATE {$qq_table} SET zid='{$row['zid']}' WHERE id='{$id}'");
	}
	exit('{"code":1,"msg":"操作成功！"}');
break;
case 'edit_protocol_sort':
	unset($_POST['frame_type']);
	foreach($_POST['sort'] as $id=>$sort){
		$DB->exec("UPDATE `pre_protocol` SET sort='$sort' WHERE id='{$id}'");
	}
	exit('{"code":1,"msg":"排序成功！"}');
break;
case 'protocol_sort':
	$type=daddslashes($_POST['type']);
	$data=$DB->getAll("SELECT * FROM pre_protocol WHERE cid='{$type}' ORDER BY sort ASC");
	exit(json_encode($data));
break;
case 'api_sort':
	$id = intval($_POST['id']);
	$sort = intval($_POST['sort']);
	$row = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$id}' LIMIT 1");
	$row2 = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE sort='{$sort}' LIMIT 1");
	if($row2){
		$DB->exec("UPDATE pre_loginconfig SET sort='{$row['sort']}' WHERE id='{$row2['id']}'");
	}
	$DB->exec("UPDATE pre_loginconfig SET sort='{$sort}' WHERE id='{$id}'");
	exit('{"code":1,"msg":"已操作!"}');
break;

case 'renew_qq':
	$biz = isset($_REQUEST['biz']) ? daddslashes($_REQUEST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$id = intval($_REQUEST['id'] ?? 0);
	$time = intval($_REQUEST['time'] ?? 0);
	if($id <= 0 || $time <= 0) exit('{"code":-1,"msg":"参数错误"}');
	$row = $DB->getRow("SELECT id,endtime_arr FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
	if(!$row) exit('{"code":-1,"msg":"数据不存在"}');
	$tmp = ['type'=>9999,'num'=>$time,'time_type'=>0];
	$t = edit_endtime($tmp,$row['endtime_arr'],'+');
	if($DB->exec("UPDATE {$qq_table} SET endtime_arr='".$t."' WHERE id='{$id}'")){
		exit('{"code":1,"msg":"操作成功"}');
	}else{
		exit('{"code":-1,"msg":"操作失败'.$DB->error().'"}');
	}
break;

case 'get_article':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;
	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT * FROM pre_article ORDER BY id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_article");
		$pages = ceil($total/$pagesize);	//总页数
	}
	$class = array();
	$rs=$DB->query("SELECT * FROM `pre_article_class` where zid='1'");
	while($res = $rs->fetch()){
		$class[$res['id']] = $res['name'];
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data,'class'=>$class)));
break;
case 'get_articleClass':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT * FROM pre_article_class ORDER BY id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_article_class");
		$pages = ceil($total/$pagesize);	//总页数
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data)));
break;
case 'account':	//修改管理员账号密码
	$user=daddslashes($_POST['admin_user']);
	$oldpwd=daddslashes($_POST['oldpwd']);
	$newpwd=daddslashes($_POST['newpwd']);
	$newpwd2=daddslashes($_POST['newpwd2']);
	$pwd = $conf['admin_pwd'];
	if($newpwd != '' || $newpwd2 != ''){
		if($oldpwd !=$pwd)exit('{"code":-1,"msg":"旧密码不正确"}');
		if($newpwd != $newpwd2){
			exit('{"code":-1,"msg":"输入的密码不配"}');
		}elseif(strlen($newpwd2) < 6) {
			exit('{"code":-1,"msg":"密码不能低于6位"}');
		}else{
			$pwd = $newpwd2;
		}
		$DB->exec("UPDATE pre_config SET v=:v WHERE k=:key", [':key'=>'admin_pwd',':v'=>$pwd]);
	}
	$DB->exec("UPDATE pre_config SET v=:v WHERE k=:key", [':key'=>'admin_user',':v'=>$user]);
	exit('{"code":1,"msg":"修改成功"}');
break;
case 'del_message':
	$id = intval($_POST['id']);
	$row=$DB->getRow("select * from pre_message where id='$id' limit 1");
	if(!$row)exit('{"code":-1,"msg":"数据不存在!"}');
	$DB->exec("DELETE FROM pre_message WHERE id='{$id}'");
	exit('{"code":1,"msg":"已操作!"}');
break;
case 'active_message':
	$id = intval($_POST['id']);
	$active=intval($_POST['zt']);
	$sql="UPDATE pre_message SET active='{$active}' WHERE id='{$id}'";
	$DB->exec($sql);
	exit('{"code":1,"msg":"已操作!"}');
break;
case 'edit_message':
	$id = intval($_POST['ids']);
	$title=daddslashes($_POST['title']);
	$type=intval($_POST['type']);
	$content=daddslashes($_POST['content']);
	if($title==NULL or $content==NULL)exit('{"code":-1,"msg":"请确保每项都不为空!"}');
	$row=$DB->getRow("select * from pre_message where id='$id' limit 1");
	if(!$row)exit('{"code":-1,"msg":"数据不存在!"}');

	if($DB->exec("update pre_message set type='$type',title='$title',content='$content' where id='{$id}'")!==false){
		exit('{"code":1,"msg":"修改成功！"}');
	}else{
		exit('{"code":1,"msg":"修改失败'.$DB->error().'"}');
	}
break;
case 'add_message':
	$title=daddslashes($_POST['title']);
	$type=intval($_POST['type']);
	$content=daddslashes($_POST['content']);
	if($title==NULL or $content==NULL)exit('{"code":-1,"msg":"请确保每项都不为空!"}');
	$row=$DB->getRow("select * from pre_message where type='$type' and title='$title' limit 1");
	if($row){
		exit('{"code":-1,"msg":"通知标题已存在！!"}');
	}else{
		$sql="insert into `pre_message` (`type`,`title`,`content`,`addtime`,`active`) values ('".$type."','".$title."','".$content."','".$date."','1')";
		if($DB->exec($sql)!==false){
			exit('{"code":1,"msg":"添加成功！"}');
		}else{
			exit('{"code":1,"msg":"添加失败'.$DB->error().'"}');
		}
	}
break;
case 'get_message':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;
	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT * FROM pre_message ORDER BY id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_message");
		$pages = ceil($total/$pagesize);	//总页数
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data)));
break;
case 'frame_conf':	//框架初始配置，全部写入 pre_config（与 site 一致）
	$exclude_keys = array('sqlkey', 'zf_key');	// 已移除的配置项，不再保存
	foreach($_POST as $k=>$v){
		if(in_array($k, $exclude_keys)) continue;
		$DB->query("REPLACE INTO pre_config SET v='".daddslashes($v)."',k='".daddslashes($k)."'");
	}
	exit('{"code":1,"msg":"修改成功"}');
break;
case 'set_loginapi':
	$type = daddslashes($_POST['type']);
	$id = daddslashes($_POST['id']);
	$zt = daddslashes($_POST['zt']);
	if($type == 'status'){//接口开关
		$sql = "UPDATE `pre_loginconfig` SET `status`='{$zt}' WHERE id='{$id}'";
	}elseif($type == 'census'){//显示人数开关
		$sql = "UPDATE `pre_loginconfig` SET `show_online_census`='{$zt}' WHERE id='{$id}'";
	}elseif($type == 'del'){//删除
		$sql = "DELETE FROM `pre_loginconfig` WHERE id='{$id}'";
	}elseif($type == 'sort'){//排序
		foreach($_POST['sort'] as $sid=>$sort){
			$DB->exec("UPDATE `pre_loginconfig` SET sort='$sort' WHERE id='{$sid}'");
		}
		exit('{"code":1,"msg":"排序成功！"}');
	}else{
		exit('{"code":-1,"msg":"类型错误"}');
	}
	$DB->exec($sql);
	exit('{"code":1,"msg":"已操作"}');
break;
case 'get_loginapi':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$offset=$pagesize*($page - 1);
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	if(!in_array($biz, ['all','cloud','dg'], true)) $biz = 'cloud';
	$where = $biz === 'all' ? "(biz='cloud' OR biz='dg')" : "biz='{$biz}'";
	$rs=$DB->query("SELECT * FROM pre_loginconfig WHERE {$where} ORDER BY sort ASC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_loginconfig WHERE {$where}");
		$pages = ceil($total/$pagesize);	//总页数
	}
	$class=$DB->getAll("SELECT * FROM pre_login_class WHERE ".($biz === 'all' ? "(biz='cloud' OR biz='dg')" : "biz='{$biz}'"));
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data,'class'=>$class)));
break;
case 'edit_protocol':
	foreach($_POST['name'] as $id=>$name){
		$sort = intval($_POST['sort_p'][$id]);
		if($_POST['status'][$id]=='on'){
			$status = '1';
		}else{
			$status = '0';
		}
		$is_pwd = intval($_POST['is_pwd'][$id]);
		$type = intval($_POST['type'][$id]);
		$DB->exec("UPDATE `pre_protocol` SET is_pwd='{$is_pwd}',sort='{$sort}',name='{$name}',status='{$status}',type='{$type}' WHERE id='{$id}'");
	}
	exit('{"code":1,"msg":"已操作"}');
break;
case 'add_protocol':
	$name = daddslashes($_POST['name']);
	$type = intval($_POST['type']);
	$cid = daddslashes($_GET['cid']);
	$row=$DB->getRow("SELECT sort FROM pre_protocol WHERE cid='{$cid}' ORDER BY sort DESC LIMIT 1");
	$DB->exec("INSERT INTO `pre_protocol` (`sort`,`cid`,`name`,`type`) VALUES ('".($row['sort']+1)."', '".$cid."', '".$name."', ".$type.")");
	exit('{"code":1,"msg":"已添加"}');
break;
case 'get_protocol':
	$arr=$DB->getAll("SELECT * FROM pre_protocol WHERE cid='{$_GET['type']}' ORDER BY cid,sort ASC");
	exit(json_encode($arr));
break;
case 'get_record':
	$pagesize = isset($_GET['pagesize'])?intval($_GET['pagesize']):30;
	$page = isset($_GET['page'])?intval($_GET['page']):1;
	$offset = ($page - 1) * $pagesize;
	$type=isset($_GET['type'])?daddslashes($_GET['type']):'-1';
	$kw=isset($_GET['kw'])?daddslashes($_GET['kw']):'';
	if($kw != ''){
		$sql =" zid='{$kw}' OR bz like '%{$kw}%'";
	}else if($type != '-1'){
		$sql =" type='".$type."'";
	}else{
		$sql =" 1";
	}
	$rs=$DB->query("SELECT * FROM pre_points WHERE".$sql." ORDER BY id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		// 字段名已经正确，不需要映射
		$data[] = $res;
	}
	$total = $DB->getColumn("SELECT count(*) FROM pre_points WHERE".$sql);
	exit(json_encode(array('data' => $data, 'total' => intval($total))));
break;
case 'export_record':
	$type=isset($_GET['type'])?daddslashes($_GET['type']):'-1';
	$kw=isset($_GET['kw'])?daddslashes($_GET['kw']):'';
	if($kw != ''){
		$sql =" zid='{$kw}' OR bz like '%{$kw}%'";
	}else if($type != '-1'){
		$sql =" type='".$type."'";
	}else{
		$sql =" 1";
	}
	$rs=$DB->query("SELECT * FROM pre_points WHERE".$sql." ORDER BY id DESC");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$data[] = $res;
	}
	
	header('Content-Type: text/csv; charset=utf-8');
	header('Content-Disposition: attachment; filename=收支明细_'.date('YmdHis').'.csv');
	$output = fopen('php://output', 'w');
	fwrite($output, chr(0xEF).chr(0xBB).chr(0xBF));
	fputcsv($output, array('ID', '用户ID', '金额类型', '类型', '金额', '变更前', '变更后', '详情', '时间'));
	foreach($data as $row){
		// 直接使用数据库存储的rmb值，包括符号
		$rmbValue = floatval($row['rmb']) ?: 0;
		$amount = $rmbValue < 0 ? $rmbValue : '+' . $rmbValue;
		
		fputcsv($output, array(
			$row['id'],
			$row['zid'],
			$row['type2'],
			$row['type'],
			$amount,
			$row['before_rmb'],
			$row['after_rmb'],
			$row['bz'],
			$row['addtime']
		));
	}
	fclose($output);
	exit;
break;
case 'tixian_ret':	//提现退回
	$id=floatval($_POST['id']);
	$ret=daddslashes($_POST['ret']);
	if($id == '')exit('{"code":-1,"msg":"参数不正确"}');
	$row = $DB->getRow("SELECT a.*,b.rmb,b.rmb_tc FROM `pre_tixian` a LEFT JOIN `bot_user` b ON a.zid=b.zid WHERE a.id='{$id}' LIMIT 1");
	if($row){
		$DB->exec("UPDATE `pre_tixian` SET `status`='2',`remark`='{$ret}',`endtime`=NOW() WHERE id='{$id}'");
		if($row['tx_type'] == 'rmb'){
			$DB->exec("UPDATE `pre_user` SET `rmb`=`rmb`+'{$row['money']}' WHERE zid='{$row['zid']}'");
			add_money_log($row['zid'],'余额','退回余额提现',$row['money'],$row['rmb'],($row['rmb']+$row['money']),$ret);
		}elseif($row['tx_type'] == 'tc'){
			$DB->exec("UPDATE `pre_user` SET `rmb_tc`=`rmb_tc`+'{$row['money']}' WHERE zid='{$row['zid']}'");
			add_money_log($row['zid'],'佣金','退回佣金提现',$row['money'],$row['rmb_tc'],($row['rmb_tc']+$row['money']),$ret);
		}
		exit('{"code":1,"msg":"ok"}');
	}else{
		exit('{"code":-1,"msg":"订单不存在"}');
	}
break;
case 'get_tixian_detail':	//获取提现详情
	$id=floatval($_POST['id']);
	if($id == '')exit('{"code":-1,"msg":"参数不正确"}');
	$row = $DB->getRow("SELECT a.*,b.user,b.pay_img FROM pre_tixian a LEFT JOIN pre_user b ON a.zid=b.zid WHERE a.id='{$id}' LIMIT 1");
	if($row){
		exit('{"code":1,"msg":"ok","data":'.json_encode($row).'}');
	}else{
		exit('{"code":-1,"msg":"订单不存在"}');
	}
break;
case 'tixian_ok':	//确认用户提现
	$id=floatval($_POST['id']);
	if($id == '')exit('{"code":-1,"msg":"参数不正确"}');
	$row = $DB->getRow("SELECT * FROM `pre_tixian` WHERE id='{$id}' LIMIT 1");
	if($row){
		$DB->exec("UPDATE `pre_tixian` SET `status`='1',`endtime`=NOW() WHERE id='{$id}'");
		exit('{"code":1,"msg":"ok"}');
	}else{
		exit('{"code":-1,"msg":"订单不存在"}');
	}
break;
case 'get_tixian':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$type=isset($_POST['type'])?daddslashes($_POST['type']):'-1';
	$kw=isset($_POST['kw'])?daddslashes($_POST['kw']):'';
	if($kw != ''){
		$sql =" b.user='{$kw}' OR a.pay_name='{$kw}' OR a.pay_account='{$kw}'";
	}else if($type == '0' || $type == '1' || $type == '2'){
		$sql =" a.status='".$type."'";
	}else{
		$sql =" 1";
	}
	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT a.*,b.user FROM pre_tixian a LEFT JOIN pre_user b ON a.zid=b.zid WHERE".$sql." ORDER BY a.id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		if($res['upzid_all']==''){
			$agent = 1;
		}else{
			$agent = count(explode('_',$res['upzid_all']))+1;
		}
		$res['agent'] = $agent;
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_tixian WHERE".$sql);
		$pages = ceil($total/$pagesize);	//总页数
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data)));
break;
case 'get_user':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$kw=isset($_POST['kw'])?daddslashes($_POST['kw']):'';
	$vip=isset($_POST['vip'])?intval($_POST['vip']):0;

	$conditions = [];
	if($kw != ''){
		$conditions[] = "(`qq` like '%{$kw}%' OR `user`='{$kw}' OR `zid`='{$kw}')";
	}
	if($vip > 0){
		$conditions[] = "`vip`='{$vip}'";
	}
	$sql = empty($conditions) ? "1" : implode(' AND ', $conditions);

	$viparr = [];
	$rs=$DB->query("SELECT * FROM pre_user_vip");
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$viparr[$res['vip']] = $res;
	}

	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT * FROM pre_user WHERE".$sql." ORDER BY zid DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		if($res['upzid_all']==''){
			$agent = 1;
		}else{
			$agent = count(explode('_',$res['upzid_all']))+1;
		}
		$res['vipname'] = $viparr[$res['vip']]['name'];
		$res['agent'] = $agent;
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_user WHERE".$sql);
		$pages = ceil($total/$pagesize);	//总页数
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data,'vipnum'=>$DB->getColumn("SELECT count(*) FROM pre_user_vip"))));
break;
case 'batch_delqq':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$ids = $_POST['ids'];
	if(empty($ids))exit('{"code":-1,"msg":"请选择要删除的QQ"}');

	if($biz === 'dg'){
		$ids_int = array_map('intval', (array)$ids);
		$ids_int = array_values(array_filter($ids_int, function($v){ return $v > 0; }));
		if(count($ids_int) == 0) exit('{"code":-1,"msg":"请选择要删除的QQ"}');
		$idstr = implode(',', $ids_int);
		$deleted = $DB->exec("DELETE FROM {$qq_table} WHERE id IN ({$idstr})");
		$deleted = $deleted === false ? 0 : intval($deleted);
		$fail = max(0, count($ids_int) - $deleted);
		exit('{"code":1,"msg":"成功删除 '.$deleted.' 个QQ，失败 '.$fail.' 个","success_count":'.$deleted.',"fail_count":'.$fail.'}');
	}
	
	$qq_list = [];
	$login_map = [];
	
	foreach($ids as $id) {
		$id = intval($id);
		$row = $DB->getRow("SELECT id,zid,qq,cityid FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
		if($row) {
			$login = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$row['cityid']}' LIMIT 1");
			if($login){
				$qq_list[] = $row['qq'];
				$login_map[$row['cityid']] = $login;
			}
			if($biz === 'cloud'){
				set_qq_user($row['qq'],0,1);
			}else{
				$DB->exec("DELETE FROM {$qq_table} WHERE id='".intval($row['id'])."'");
			}
		}
	}
	
	if(count($qq_list) == 0)exit('{"code":-1,"msg":"没有要删除的QQ"}');
	
	$success_count = 0;
	$fail_count = 0;
	
	if($biz === 'cloud'){
	foreach($login_map as $cityid => $login) {
		$login_qqs = [];
		foreach($qq_list as $qq) {
			$row = $DB->getRow("SELECT cityid FROM `{$qq_table}` WHERE qq='{$qq}' LIMIT 1");
			if($row && $row['cityid'] == $cityid) {
				$login_qqs[] = $qq;
			}
		}
		
		if(count($login_qqs) > 0 && ($login['type'] != 'myqq' && $login['type'] != 'myqqa')){
			$ret = batch_delete_qq($login, $login_qqs, 10);
			if(is_array($ret)){
				$success = 0;
				$fail = 0;
				foreach($ret as $r){
					$bool = $r['bool'] ?? $r['bool '] ?? false;
					$retcode = $r['retcode'] ?? $r['retcode '] ?? 0;
					if($retcode == 200 && $bool === true){
						$success++;
					}else{
						$fail++;
					}
				}
				$success_count += $success;
				$fail_count += $fail;
			}else{
				$fail_count += count($login_qqs);
			}
		}
	}
	}
	
	exit('{"code":1,"msg":"成功删除 '.$success_count.' 个QQ，失败 '.$fail_count.' 个","success_count":'.$success_count.',"fail_count":'.$fail_count.'}');
break;
case 'del_frame_qq':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$id = intval($_POST['id']);
	$login = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$id}' LIMIT 1");
	if(!$login)exit('{"code":-1,"msg":"数据不存在"}');
	$rs=$DB->query("SELECT id,qq FROM {$qq_table} WHERE cityid='{$login['id']}'");
	$qqArr = array();
	while($res = $rs->fetch()){
		$qqArr[$res['qq']] = 1;
	}
	if(count($qqArr) == 0)exit('{"code":-1,"msg":"网站内没有QQ号使用该接口<br>'.$login['citybz'].'"}');
	$data = ($biz === 'cloud') ? get_frame_qq($login,'3') : [];
	if(!is_array($data) || count($data) == 0)exit('{"code":-1,"msg":"获取框架qq失败"}');
	$del_qq = array();
	if(array_key_exists('bot',$data) && isset($data['bot']['list'])){
		foreach ($data['bot']['list'] as $item){
			$qq = isset($item['bot']) ? $item['bot'] : (isset($item['uin']) ? $item['uin'] : '');
			if($qq && is_numeric($qq) && !isset($qqArr[$qq])){
				$del_qq[] = $qq;
			}
		}
	}elseif(array_key_exists('QQlist',$data)){
		foreach ($data['QQlist'] as $qq => $v){
			if(!isset($qqArr[$qq]) && is_numeric($qq))$del_qq[]=$qq;
		}
	}else{
		foreach ($data as $qq => $v){
			if(!isset($qqArr[$qq]) && is_numeric($qq))$del_qq[]=$qq;
		}
	}
	if(count($del_qq) != 0){
		batch_delete_qq($login,$del_qq);
		exit('{"code":1,"msg":"成功删除 '.count($del_qq).' 个QQ"}');
	}else{
		exit('{"code":1,"msg":"没有要删除的QQ"}');
	}
break;
case 'del_user':
	$zid = intval($_POST['zid']);
	$type = intval($_POST['type']);
	$del_type = intval($_POST['del_type']);
	if($type == 1){//删除所有 没QQ数据的用户 
		$time = time();
		$arr_id = array();
		$del_id = array();
		$sql = " '".$time."' > a.uptime";
		$rs=$DB->query("SELECT a.zid,a.rmb,a.rmb_tc, COUNT(b.qq) AS qq_count FROM pre_user a LEFT JOIN pre_qq b ON a.zid = b.zid WHERE".$sql." GROUP BY a.zid LIMIT 5000");
		while($res = $rs->fetch()){
			if(intval($res['qq_count']) == 0){
				if($del_type==1){//删除所有
					$del_id[] = $res['zid'];
				}else{
					if(floatval($res['rmb'])>0 || floatval($res['rmb_tc'])>0){
						$arr_id[] = $res['zid'];
					}else{
						$del_id[] = $res['zid'];
					}
				}
			}else{
				$arr_id[] = $res['zid'];
			}
		}
		if(count($del_id)>0){
			$DB->exec("DELETE FROM `pre_user` WHERE zid IN (".implode(',',$del_id).")");
		}
		if(count($arr_id)>0){
			$t = strtotime("+2 hour");
			$DB->exec("UPDATE pre_user SET uptime='".$t."' WHERE zid IN(".implode(',',$arr_id).")");
		}
	}else{//删除单个用户
		$row = $DB->getRow("SELECT zid,user FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
		if(!$row)exit('{"code":-1,"msg":"数据不存在"}');
		$DB->exec("DELETE FROM `pre_user` WHERE zid='{$zid}'");
	}
	exit('{"code":1,"msg":"操作成功"}');
break;
case 'del_time'://删除QQ代挂时长
	$biz = isset($_REQUEST['biz']) ? daddslashes($_REQUEST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$id = intval($_REQUEST['id'] ?? 0);
	$cids = intval($_REQUEST['cids'] ?? 0);
	if($id <= 0 || $cids <= 0){
		exit('{"code":-1,"msg":"参数错误"}');
	}
	$row = $DB->getRow("SELECT * FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
	if(!$row){
		$other_biz = $biz === 'dg' ? 'cloud' : 'dg';
		$other_table = $other_biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
		$other = $DB->getRow("SELECT id FROM `{$other_table}` WHERE id='{$id}' LIMIT 1");
		if($other){
			exit('{"code":-1,"msg":"当前数据版本不匹配，请切换到'.($other_biz==='dg'?'代挂':'云端').'列表后操作"}');
		}
		exit('{"code":-1,"msg":"数据不存在"}');
	}
	$arr = json_decode($row['endtime_arr'],true);
	if(!is_array($arr)) $arr = [];

	$sim = $arr;
	unset($sim[$cids]);
	if($cids < 1000 && !isset($sim['1000'])){
		$sim['1000'] = $date;
	}
	if(count($sim) < 1){
		exit('{"code":-1,"msg":"最少保留一个分类"}');
	}

	unset($arr[$cids]);
	if($cids < 1000 && !isset($arr['1000'])){
		$arr['1000'] = $date;
	}
	$t = json_encode($arr);
	$DB->exec("UPDATE {$qq_table} SET endtime_arr='".$t."' WHERE id='{$id}'");
	exit('{"code":1,"msg":"操作成功"}');
break;
case 'edit_endtime':	//续费QQ时长data : 
	$biz = isset($_REQUEST['biz']) ? daddslashes($_REQUEST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$id = intval($_REQUEST['id'] ?? 0);
	$cids = intval($_REQUEST['cids'] ?? 0);
	$num = isset($_REQUEST['num']) ? daddslashes($_REQUEST['num']) : '';
	$renew_type = (isset($_REQUEST['renew_type']) && $_REQUEST['renew_type'] == 0) ? '+' : '-';	//增加/扣除
	if($id <= 0 || $cids <= 0 || $num === ''){
		exit('{"code":-1,"msg":"参数错误"}');
	}
	$qqrow = $DB->getRow("SELECT id,qq,endtime_arr FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
	if(!$qqrow){
		$other_biz = $biz === 'dg' ? 'cloud' : 'dg';
		$other_table = $other_biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
		$other = $DB->getRow("SELECT id FROM `{$other_table}` WHERE id='{$id}' LIMIT 1");
		if($other){
			exit('{"code":-1,"msg":"当前数据版本不匹配，请切换到'.($other_biz==='dg'?'代挂':'云端').'列表后操作"}');
		}
		exit('{"code":-1,"msg":"数据不存在"}');
	}
	$tmp = array(
		'type'=>$cids,
		'num'=>$num,
		'time_type'=>intval($_REQUEST['type'] ?? 0),
	);
	$t = edit_endtime($tmp,$qqrow['endtime_arr'],$renew_type);
	if($DB->exec("UPDATE {$qq_table} SET endtime_arr='".$t."' WHERE id='{$id}'")){
		exit('{"code":1,"msg":"操作成功"}');
	}else{
		exit('{"code":-1,"msg":"操作失败'.$DB->error().'"}');
	}
break;
case 'get_qqlist':
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$kw=isset($_POST['kw'])?daddslashes($_POST['kw']):'';
	$type=isset($_POST['type'])?daddslashes($_POST['type']):'-1';
	if($kw != ''){
		if($type == '2'){
			$sql =" a.cityid='{$kw}'";
		}elseif($type == '4'){
			$like = "'%\"".strval($kw)."\":%'";
			$sql =" a.endtime_arr like ".$like;
		}else{
			$sql =" a.qq like '%{$kw}%'";
		}
	}else if($type == '1'){
		$sql =" a.login_zt='1'";
	}else if($type == '0'){
		$sql =" a.login_zt='0'";
	}else if($type == '3'){ 	//过期的
		$sql =" a.login_zt='0'";
		$pagesize = 1000;
	}else{
		$sql =" 1";
	}
	// 获取登录配置信息
	$login_configs = array();
	$config_rs = $DB->query("SELECT id,citybz FROM pre_loginconfig WHERE biz='{$biz}'");
	while($config_res = $config_rs->fetch()){
		$login_configs[$config_res['id']] = $config_res['citybz'];
	}
	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT a.*,b.citybz FROM {$qq_table} a LEFT JOIN pre_loginconfig b ON a.cityid=b.id AND b.biz='{$biz}' WHERE".$sql." ORDER BY id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$endtime = is_end3($res['endtime_arr'])[0];
		if($type == '3'){ 	//过期
			if($date > $endtime){
				$res['endtime'] = $endtime;
				$endtime_arr = is_end3($res['endtime_arr'],1);
				$res['endtime_arr'] = $endtime_arr;
				$data[] = $res;  
			}
			continue;
		}
		$res['endtime'] = $endtime;
		$endtime_arr = is_end3($res['endtime_arr'],1);
		$res['endtime_arr'] = $endtime_arr;
		$data[] = $res;
	}
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		if($type == '3'){
			$total = count($data);
		}else{
			$total = $DB->getColumn("SELECT count(*) FROM {$qq_table} a LEFT JOIN pre_loginconfig b ON a.cityid=b.id AND b.biz='{$biz}' WHERE".$sql);
		}
		$pages = ceil($total/$pagesize);	//总页数
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data)));
break;
case 'del_kami':
	$id = intval($_POST['id']);
	$row = $DB->getRow("SELECT * FROM `pre_km` WHERE id='{$id}' LIMIT 1");
	if($row){
		// 如果是用户生成的未使用卡密，需要退回余额
		if($row['zid'] != 1 && $row['isuse'] == 0){
			$user = $DB->getRow("SELECT * FROM pre_user WHERE zid='{$row['zid']}' LIMIT 1");
			if($user){
				$before_rmb = $user['rmb'];
				$DB->exec("UPDATE pre_user SET `rmb`=`rmb`+'{$row['rmb']}' WHERE zid='{$row['zid']}'");
				add_money_log($row['zid'],'余额','退款',$row['rmb'],$before_rmb,($before_rmb+$row['rmb']),'管理员删除卡密:'.$row['km'].'退回'.$row['rmb'].'元');
			}
		}
		$DB->exec("delete from `pre_km` where id='{$id}'");
		exit('{"code":1,"msg":"删除成功"}');
	}else{
		exit('{"code":-1,"msg":"卡密信息不存在，或已经被删除"}');
	}
break;
case 'add_kami':
	$id = intval($_POST['goods']);
	$cid = intval($_POST['cid']);//9999是全部接口
	$num = intval($_POST['num']);
	$kmqz = daddslashes($_POST['kmqz']);
	$time = intval($_POST['time']);
	$time_num = intval($_POST['time_num']);
	$rmb = floatval($_POST['rmb']);
	$version = isset($_POST['version']) ? daddslashes($_POST['version']) : 'cloud';
	$card_type = isset($_POST['card_type']) ? intval($_POST['card_type']) : 0;
	if($version == 'all') $version = 'cloud_all';
	if (!in_array($version, ['cloud', 'dg', 'qzone', 'cloud_all', 'dg_all', 'tri_all'], true)) $version = 'cloud';
	if($kmqz != '' && !preg_match('/^[0-9a-zA-Z]+$/',$kmqz))exit('{"code":-1,"msg":"卡密前缀仅支持数字、英文"}');
	$time_arr = [0=>'月',1=>'天',2=>'时'];
	if($id == -1){
	if($cid == 9999){
		$name = '全部接口_'.$time_num.$time_arr[$time];
	}else{
		$class_info = $DB->getRow("SELECT * FROM pre_login_class WHERE id='{$cid}' LIMIT 1");
		if(!$class_info)exit('{"code":-1,"msg":"商品不存在"}');
		$name = '自定义_'.$time_num.$time_arr[$time].'_'.$class_info['name'];
	}
}elseif($rmb > 0){
	$name = '余额卡密_'.$rmb.'元';
}else{
	$time_num = 0;
	$goods_info = $DB->getRow("SELECT a.*,b.name as name2 FROM pre_goods a LEFT JOIN pre_login_class b ON a.type=b.id WHERE a.id='{$id}' LIMIT 1");
	if(intval($goods_info['type']) === 9999){
		$name = $goods_info['name'].'_全部接口';
	}else{
		$name = isset($goods_info['name2'])?$goods_info['name'].'_'.$goods_info['name2']:$goods_info['name'];
	}
	if(!$goods_info)exit('{"code":-1,"msg":"商品不存在"}');
	if($goods_info['status'] != '1')exit('{"code":-1,"msg":"商品已下架"}');
}
	$success = 0;
	$kmArr = array();
	for ($i=0; $i < $num; $i++) {
		$km = ($kmqz==''?'':$kmqz.'_').($id==-1?'':$id).random(15);
		$kmArr[] = $km;
		// 自定义卡密时，shopid保存分类ID(cid)，激活时根据cid获取分类信息
		$shopid_value = ($id == -1) ? $cid : $id;
		// 后台生成的卡密没有上级，tc_rmb为0
		$sql="INSERT INTO `pre_km` (`zid`,`km`,`name`,`shopid`,`addtime`,`time`,`time_num`,`rmb`,`tc_rmb`,`version`,`card_type`) VALUES ('1','".$km."','".$name."','".$shopid_value."', NOW(),'".$time."','".$time_num."','".$rmb."','0','".$version."','".$card_type."')";
		if($DB->exec($sql)){
			$success++;
		}
	}
	exit('{"code":1,"msg":"生成了 '.$success.' 个","km":"'.implode('\r\n',$kmArr).'"}');
break;
case 'get_kami':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):30;
	$page=isset($_POST['page'])?intval($_POST['page']):1;//当前页数的数据
	$kw=isset($_POST['kw'])?daddslashes($_POST['kw']):'';
	$type=isset($_POST['type'])?daddslashes($_POST['type']):'-2';
	if($type == '-2'){	//全部除自己
		$sql = " `zid`!='1'";
	}elseif($type == '0'){	//未使用
		$sql = " `zid`!='1' AND isuse='0'";
	}elseif($type == '1'){	//已使用
		$sql = " `zid`!='1' AND isuse='1'";
	}elseif($type == '2'){
		$sql = " `zid`='1'";
	}elseif($type == '3'){
		$sql = " `zid`='1' AND isuse='1'";
	}elseif($type == '4'){
		$sql = " `zid`='1' AND isuse='0'";
	}elseif($type == '5'){	//常规卡
		$sql = " card_type='0'";
	}elseif($type == '6'){	//特殊卡
		$sql = " card_type='1'";
	}else{	//全部
		$sql = " 1=1";
	}
	if($kw != ''){
		$sql .= " AND `km` like'%{$kw}%'";
	}
	$total = $DB->getColumn("SELECT count(*) FROM pre_km WHERE".$sql);
	$pages = ceil($total/$pagesize);
	if($page > $pages && $pages > 0) $page = $pages;
	$offset=$pagesize*($page - 1);
	$rs=$DB->query("SELECT * FROM pre_km WHERE".$sql." ORDER BY id DESC LIMIT {$offset},{$pagesize}");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		unset($res['goodsdata']);
		$price = unserialize($res['price']);
		if(is_array($price)){
			$res['price'] = json_encode($price);
		}else{
			$res['price'] = '';
		}
		$data[] = $res;
	}
	exit(json_encode(array('total' => $total,'page' => $page,'end' => $pages,'data'=>$data)));
break;
case 'clear_kami_special':
	// 清空所有特殊卡
	$DB->exec("DELETE FROM pre_km WHERE card_type='1'");
	exit('{"code":1,"msg":"清空成功"}');
break;
case 'get_goods_kami':	//商品列表可以写入 缓存
	$data = array();
	$code = -1;
	$class=[];
	$rs2=$DB->query("SELECT * FROM pre_login_class");
	while($res2 = $rs2->fetch()){
		$class[$res2['id']] = $res2['name'];
	}
	$rs=$DB->query("SELECT * FROM pre_goods WHERE status='1' ORDER BY sort ASC");
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		if($res['type'] == '99'){//用户等级
			continue;
		}
		if(!isset($res['version']) || $res['version'] === '') $res['version'] = 'cloud';
		$ver = strtolower((string)$res['version']);
		if($ver === 'tri_all'){
			$type_cloud = intval($res['type_cloud'] ?? $res['type']);
			$type_dg = intval($res['type_dg'] ?? 0);
			if(isset($class[$type_cloud]) || $type_cloud === 9999){
				if(!isset($data[$type_cloud])) $data[$type_cloud] = array();
				$data[$type_cloud][] = $res;
			}
			if($type_dg > 0 && (isset($class[$type_dg]) || $type_dg === 9999)){
				if(!isset($data[$type_dg])) $data[$type_dg] = array();
				$data[$type_dg][] = $res;
			}
		}else{
			if(isset($class[$res['type']]) || intval($res['type']) === 9999){
				if(!isset($data[$res['type']])) $data[$res['type']] = array();
				$data[$res['type']][] = $res;
			}
		}
		if($ver === 'qzone'){
			if(!isset($data['qzone'])) $data['qzone'] = array();
			$data['qzone'][] = $res;
		}
	}
	if(count($data)!=0)$code = 1;
	exit(json_encode(array('code' => $code,'data' => $data,'class' => $class)));
break;
case 'get_carousel':
	$pagesize = isset($_POST['pagesize'])?intval($_POST['pagesize']):10;
	$page=isset($_POST['page'])?intval($_POST['page']):1;
	$keyword=isset($_POST['keyword'])?daddslashes($_POST['keyword']):'';
	$offset=$pagesize*($page - 1);
	
	$sql = "1";
	if($keyword != ''){
		$sql .= " AND title LIKE '%{$keyword}%'";
	}
	
	$rs=$DB->query("SELECT * FROM pre_carousel WHERE {$sql} ORDER BY sort ASC, id DESC LIMIT $offset,$pagesize");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		$data[] = $res;
	}
	
	if(count($data) == 0){
		$total = 0;
		$pages = 1;
	}else{
		$total = $DB->getColumn("SELECT count(*) FROM pre_carousel WHERE {$sql}");
		$pages = ceil($total/$pagesize);
	}
	exit(json_encode(array('code' => 1, 'total' => $total, 'page' => $page, 'end' => $pages, 'data'=>$data)));
break;
case 'get_carousel_detail':
	$id = intval($_POST['id']);
	$row = $DB->getRow("SELECT * FROM pre_carousel WHERE id='{$id}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"轮播图不存在"}');
	exit(json_encode(array('code' => 1, 'data' => $row)));
break;
case 'save_carousel':
	$id = intval($_POST['id']);
	$title = daddslashes($_POST['title']);
	$link = daddslashes($_POST['link']);
	$sort = intval($_POST['sort']);
	$status = intval($_POST['status']);
	$type = intval($_POST['type']);
	
	if($title == '')exit('{"code":-1,"msg":"标题不能为空"}');
	
	$image = '';
	if($type == 0){
		$image = daddslashes($_POST['image']);
		if($image == '')exit('{"code":-1,"msg":"图片地址不能为空"}');
	}else{
		// 处理图片上传
		if(isset($_FILES['file']) && $_FILES['file']['error'] == 0){
			$allowedTypes = array('image/jpeg', 'image/png', 'image/gif');
			if(!in_array($_FILES['file']['type'], $allowedTypes)){
				exit('{"code":-1,"msg":"只支持 JPG、PNG、GIF 格式图片"}');
			}
			
			$maxSize = 10 * 1024 * 1024; // 5MB
			if($_FILES['file']['size'] > $maxSize){
				exit('{"code":-1,"msg":"图片大小不能超过 10MB"}');
			}
			
			// 创建上传目录
			$uploadDir = dirname(__DIR__) . '/assets/upload/carousel/';
			if(!is_dir($uploadDir)){
				mkdir($uploadDir, 0755, true);
			}
			
			// 生成唯一文件名
			$filename = date('YmdHis') . '_' . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
			$filepath = $uploadDir . $filename;
			
			if(move_uploaded_file($_FILES['file']['tmp_name'], $filepath)){
				$image = '/assets/upload/carousel/' . $filename;
			}else{
				exit('{"code":-1,"msg":"图片上传失败"}');
			}
		}else{
			exit('{"code":-1,"msg":"请选择图片文件"}');
		}
	}
	
	if($id > 0){
		// 更新
		$row = $DB->getRow("SELECT * FROM pre_carousel WHERE id='{$id}' LIMIT 1");
		if(!$row)exit('{"code":-1,"msg":"轮播图不存在"}');
		
		// 如果是上传类型且图片有变化，删除旧图片
		if($type == 1 && $row['type'] == 1 && $row['image'] != $image){
			$oldImage = dirname(__DIR__) . $row['image'];
			if(file_exists($oldImage)){
				unlink($oldImage);
			}
		}
		
		$DB->exec("UPDATE pre_carousel SET title='{$title}', image='{$image}', link='{$link}', sort='{$sort}', status='{$status}', type='{$type}' WHERE id='{$id}'");
		exit('{"code":1,"msg":"更新成功"}');
	}else{
		// 添加
		$DB->exec("INSERT INTO pre_carousel (title, image, link, sort, status, type, addtime) VALUES ('{$title}', '{$image}', '{$link}', '{$sort}', '{$status}', '{$type}', NOW())");
		exit('{"code":1,"msg":"添加成功"}');
	}
break;
case 'delete_carousel':
	$id = intval($_POST['id']);
	$row = $DB->getRow("SELECT * FROM pre_carousel WHERE id='{$id}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"轮播图不存在"}');
	
	// 如果是上传类型，删除图片文件
	if($row['type'] == 1){
		$imagePath = dirname(__DIR__) . $row['image'];
		if(file_exists($imagePath)){
			unlink($imagePath);
		}
	}
	
	$DB->exec("DELETE FROM pre_carousel WHERE id='{$id}'");
	exit('{"code":1,"msg":"删除成功"}');
break;
case 'getapi':
	$rs=$DB->query("SELECT * FROM `pre_loginconfig` WHERE status='1'");
	$data = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		if($res['exclusive'] != '' ){
			$tmp = explode('-',$res['exclusive']);
			if(in_array($res['id'],$tmp)){	//专属接口
				$data['3'][] = $res;
			}
		}elseif($res['type2'] == '1'){//独享
			$data['1'][] = $res;
		}elseif($res['type2'] == '2'){//独享+默认
			$data['2'][] = $res;
		}else{
			$data['0'][] = $res;
		}
	}

	//获取协议
	$rs=$DB->query("SELECT * FROM `pre_protocol` WHERE status='1'");
	$data2 = array();
	while($res = $rs->fetch(PDO::FETCH_ASSOC)){
		foreach (framelist() as $key => $value) {
			if($key == $res['cid']){
				$data2[$key][] = array('type' => $res['type'], 'name' => $res['name']);
			}
		}
	}	
	exit(json_encode(array('data' => $data,'protocol' => $data2)));

break;
case 'edit_loginapi':
	$id = intval($_POST['id']);
	unset($_POST['id']);
	$_POST['url'] = isset($_POST['url']) ? trim($_POST['url']) : '';
	if($_POST['url'] === '') exit('{"code":-1,"msg":"URL不能为空"}');
	if(mb_substr($_POST['url'],-1) == '/')$_POST['url']=rtrim($_POST['url'],'/');
	$part = parse_url($_POST['url']);
	if(!$part || empty($part['scheme']) || empty($part['host'])) exit('{"code":-1,"msg":"URL格式错误"}');
	$port = isset($part['port']) ? intval($part['port']) : (strtolower($part['scheme']) === 'https' ? 443 : 80);
	$row = $DB->getRow("SELECT * FROM pre_loginconfig WHERE (url=:u1 OR url=:u2) AND id<>:id LIMIT 1", [':u1'=>$_POST['url'], ':u2'=>$_POST['url'].'/', ':id'=>$id]);
	if($row)exit('{"code":-1,"msg":"URL重复了请换一个。"}');
	$_POST['port'] = $port;
	$_POST['cid'] = $_POST['cid']==-1?'':$_POST['cid'];
	$key = array();
	$data = array();
	foreach ($_POST as $k => $v) {
		$key[] = '`'.$k.'`=:'.$k;
		$data[':'.$k] = $v;
	}
	$sql = "UPDATE `pre_loginconfig` SET ".implode(',',$key)." WHERE id='{$id}'";
	if($DB->exec($sql,$data)){
		exit('{"code":1,"msg":"修改成功"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}
break;
case 'add_loginapi':
	$_POST['url'] = isset($_POST['url']) ? trim($_POST['url']) : '';
	if($_POST['url'] === '') exit('{"code":-1,"msg":"URL不能为空"}');
	if(mb_substr($_POST['url'],-1) == '/')$_POST['url']=rtrim($_POST['url'],'/');
	$part = parse_url($_POST['url']);
	if(!$part || empty($part['scheme']) || empty($part['host'])) exit('{"code":-1,"msg":"URL格式错误"}');
	$port = isset($part['port']) ? intval($part['port']) : (strtolower($part['scheme']) === 'https' ? 443 : 80);
	$row = $DB->getRow("SELECT * FROM pre_loginconfig WHERE (url=:u1 OR url=:u2) LIMIT 1", [':u1'=>$_POST['url'], ':u2'=>$_POST['url'].'/']);
	if($row)exit('{"code":-1,"msg":"URL重复了请换一个。"}');
	$_POST['port'] = $port;
	$_POST['cid'] = $_POST['cid']==-1?'':$_POST['cid'];
	$skey = array();
	$value = array();
	foreach ($_POST as $k => $v) {
		$skey[] = '`'.$k.'`';
		$value[] = "'".$v."'";
	}
	if($DB->exec("INSERT INTO `pre_loginconfig` (".implode(',',$skey).") VALUES (".implode(',',$value).")")){
		exit('{"code":1,"msg":"添加成功"}');
	}else{
		exit('{"code":-1,"msg":"添加失败'.$DB->error().'"}');
	}
break;
case 'set':	//网站信息配置
	$qqs = trim($_POST['qq'],'ok');
	$tmp = ['694319649','1443062747'];
	if(in_array($qqs,$tmp)){
		if($qqs.'ok' != $_POST['qq']){
			exit('{"code":-1,"msg":"'.$_POST['qq'].'该QQ账号不能使用"}');
		}
		$_POST['qq']=$qqs;
	}
	if(isset($_POST['task_num'])){
		$_POST['task_num'] = intval($_POST['task_num'])<=0?50:intval($_POST['task_num']);
	}
	foreach($_POST as $k=>$v){
		// 统计代码保存为原始 HTML，不经过 htmlspecialchars，否则前端无法执行
		// 邮件/微信模板等：允许保存原始 HTML（后台可控）
		if($k === 'tongji_code'
			|| $k === 'qq_end_notify_nr'
			|| $k === 'notice_qqzt_nr'
			|| $k === 'notice_recharge_tpl'
			|| $k === 'notice_consume_tpl'
			|| $k === 'notice_online_buy_tpl'
		){
			$v = is_string($v) ? addslashes(trim($v)) : '';
		}else{
			$v = daddslashes($v);
		}
		$DB->query("REPLACE INTO pre_config SET v='".$v."',k='".daddslashes($k)."'");
	}
	exit('{"code":1,"msg":"修改成功"}');
break;
//////////////////////////////////////////////////////////////////////////////////以上是新的
case 'article_upload':
	$file_name = $_FILES['imgFile']['name'];
	$tmp_name = $_FILES['imgFile']['tmp_name'];
	//获得文件扩展名
	$temp_arr = explode(".", $file_name);
	$file_ext = array_pop($temp_arr);
	$file_ext = strtolower(trim($file_ext));
	if (in_array($file_ext, array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp')) === false) {
		exit('{"error":1,"message":"上传文件扩展名是不允许的扩展名。"}');
	}
	if(!file_exists(ROOT.'assets/img/article')){
		if(!mkdir(ROOT.'assets/img/article')){
			exit('{"error":1,"message":"创建文件夹失败，请确保有写入的权限"}');
		}
	}
	$filename = md5_file($tmp_name).'.'.$file_ext;
	$fileurl = '/assets/img/article/'.$filename;
	if(copy($tmp_name, ROOT.'assets/img/article/'.$filename)){
		exit('{"error":0,"url":"'.$fileurl.'"}');
	}else{
		exit('{"error":1,"message":"上传失败，请确保有本地写入权限"}');
	}
break;
case 'upload_logo':
	// 上传网站Logo
	if(!isset($_FILES['file']) || $_FILES['file']['error'] != 0){
		exit('{"code":-1,"msg":"请选择图片文件"}');
	}
	
	$file_name = $_FILES['file']['name'];
	$tmp_name = $_FILES['file']['tmp_name'];
	$file_size = $_FILES['file']['size'];
	
	// 检查文件大小 (2MB)
	$maxSize = 2 * 1024 * 1024;
	if($file_size > $maxSize){
		exit('{"code":-1,"msg":"图片大小不能超过 2MB"}');
	}
	
	// 获得文件扩展名
	$temp_arr = explode(".", $file_name);
	$file_ext = array_pop($temp_arr);
	$file_ext = strtolower(trim($file_ext));
	
	// 检查文件类型
	$allow_exts = array('gif', 'jpg', 'jpeg', 'png', 'svg', 'webp', 'ico');
	if (!in_array($file_ext, $allow_exts)) {
		exit('{"code":-1,"msg":"只支持 jpg/png/gif/svg/webp/ico 格式"}');
	}
	
	// 创建上传目录
	$uploadDir = ROOT . 'assets/upload/logo/';
	if(!is_dir($uploadDir)){
		mkdir($uploadDir, 0755, true);
	}
	
	// 生成唯一文件名
	$filename = 'logo_' . date('YmdHis') . '_' . uniqid() . '.' . $file_ext;
	$filepath = $uploadDir . $filename;
	
	if(move_uploaded_file($tmp_name, $filepath)){
		$url = '/assets/upload/logo/' . $filename;
		exit('{"code":1,"msg":"上传成功","url":"' . $url . '"}');
	}else{
		exit('{"code":-1,"msg":"上传失败，请检查目录权限"}');
	}
break;
case 'fenzhan_template':
	$v = intval($_POST['value']);
	$DB->query("REPLACE INTO pre_config SET v='".$v."',k='fenzhan_template'");
	exit('{"code":1,"msg":"修改成功"}');
break;

case 'up_login':
	$id=daddslashes($_POST['id']);
	$sql = "UPDATE `pre_loginconfig` SET sort='{$_POST['sort']}',url='{$_POST['url']}',citybz='{$_POST['citybz']}',status='{$_POST['status']}',show_online_census='{$_POST['show_online_census']}',max='{$_POST['max']}' WHERE id='{$id}'";
	if($DB->exec($sql)){
		exit('{"code":1,"msg":"修改成功"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}
break;
case 'show_login':
	$id=daddslashes($_POST['id']);
	$row = $DB->getRow("SELECT * FROM `pre_loginconfig` WHERE id='{$id}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"数据不存在"}');
	$shoppwd = '';
	$html = '<form id="logininfo"><input type="hidden" name="id" value="'.$id.'">
	<div class="input-group mb-5"><span class="input-group-addon">排序</span>
		<input type="text" name="sort" value="'.$row['sort'].'" class="form-control" placeholder="越少越靠前">
	</div>
	<div class="input-group mb-5"><span class="input-group-addon">登录URL</span>
		<input type="text" name="url" value="'.$row['url'].'" class="form-control">
	</div>
	<div class="input-group mb-5"><span class="input-group-addon">登录地区备注</span>
		<input type="text" name="citybz" value="'.$row['citybz'].'" class="form-control">
	</div>
	<div class="input-group mb-5"><span class="input-group-addon">状态</span>
		<select name="status" class="form-control" default="'.$row['status'].'">
			<option value="1">开启中</option>
			<option value="0">关闭中</option>
		</select>
	</div>
	<div class="input-group mt-5"><span class="input-group-addon">在线人数大于</span>
		<input type="text" name="max" class="form-control" value="'.$row['max'].'" placeholder="填写0不禁止">
		<span class="input-group-addon">个时禁止登录</span>
	</div>
	<font color="red">填写0则不禁止</font>
	<div class="input-group mb-5"><span class="input-group-addon">是否显示在线人数</span>
		<select name="show_online_census" class="form-control" default="'.$row['show_online_census'].'">
			<option value="1">开启</option>
			<option value="0">关闭</option>
		</select>
	</div>
	'.$shoppwd.'
	</form>';
	exit($html);
break;
case 'batch_set_vip':
	$vip = intval($_POST['text']);
	$num = $DB->getColumn("SELECT count(*) FROM pre_user_vip");
	if($vip<0){
		$vip = 1;
	}elseif($vip>$num){
		$vip = $num;
	}
	foreach($_POST['checkbox'] as $zid){
		$DB->exec("UPDATE pre_user SET vip='{$vip}' WHERE zid='{$zid}'");
	}
	exit('{"code":1,"msg":"修改成功"}');
break;
case 'batch_change_vip':
	$from_vip = intval($_POST['from_vip'] ?? 0);
	$to_vip = intval($_POST['to_vip'] ?? 0);
	$vipnum = $DB->getColumn("SELECT count(*) FROM pre_user_vip");
	if($from_vip < 1 || $to_vip < 1 || $from_vip > $vipnum || $to_vip > $vipnum){
		exit('{"code":-1,"msg":"等级范围无效"}');
	}
	if($from_vip == $to_vip){
		exit('{"code":-1,"msg":"原等级与目标等级不能相同"}');
	}
	$affect = $DB->exec("UPDATE pre_user SET vip='{$to_vip}' WHERE vip='{$from_vip}'");
	exit('{"code":1,"msg":"成功将 '.$affect.' 个用户从VIP'.$from_vip.' 修改为 VIP'.$to_vip.'"}');
break;
case 'set_vip':	//设置用户VIP等级
	$zid = intval($_POST['zid']);
	$vip = intval($_POST['vip']);
	$num = $DB->getColumn("SELECT count(*) FROM pre_user_vip");
	if($vip<0){
		$vip = 1;
	}elseif($vip>$num){
		$vip = $num;
	}
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"用户数据不存在"}');
	if($DB->exec("UPDATE pre_user SET vip='{$vip}' WHERE zid='{$zid}'")){
		exit('{"code":1,"msg":"修改成功"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}
break;
case 'edit_user':
	$zid = intval($_POST['zid']);
	if($_POST['pwd'] == '')unset($_POST['pwd']);
	unset($_POST['zid']);
	$vipnum = $DB->getColumn("SELECT count(*) FROM pre_user_vip");
	if(intval($_POST['vip'])<1){
		$_POST['vip'] = '1';
	}elseif(intval($_POST['vip'])>$vipnum){
		$_POST['vip'] = $vipnum;
	}
	$user = $_POST['user'];
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE user='{$user}' LIMIT 1");
	if($row && $row['zid'] != $zid)exit('{"code":-1,"msg":"该用户名：'.$user.'<br>已重复，请换一个。"}');
	$key = array();
	$data = array();
	foreach ($_POST as $k => $v) {
		$key[] = '`'.$k.'`=:'.$k;
		$data[':'.$k] = $v;
	}
	$sql = "UPDATE `pre_user` SET ".implode(',',$key)." WHERE zid='{$zid}'";
	if($DB->exec($sql,$data)){
		exit('{"code":1,"msg":"修改成功"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}
break;
case 'set_user_domain':	//修改用户绑定域名
	$zid=daddslashes($_POST['zid']);
	$domain=daddslashes($_POST['domain']);
	$domain2=daddslashes($_POST['domain2']);
	if($domain != ''){
		$row = $DB->getRow("SELECT * FROM `pre_user` WHERE domain='{$domain}' OR domain2='{$domain}' LIMIT 1");
		if($row){
			exit('{"code":-1,"msg":"'.$domain.'此域名已绑定在其他用户里"}');
		}
	}
	if($domain2 != ''){
		$row2 = $DB->getRow("SELECT * FROM `pre_user` WHERE domain='{$domain2}' OR domain2='{$domain2}' LIMIT 1");
		if($row2){
			exit('{"code":-1,"msg":"'.$domain2.'此域名已绑定在其他用户里"}');
		}
	}
	$sql = "UPDATE `pre_user` SET `domain`='{$domain}',`domain2`='{$domain2}' WHERE zid='{$zid}'";
	if($DB->exec($sql)){
		exit('{"code":1,"msg":"修改成功"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}	
break;
case 'set_user_pwd':	//重置用户密码
	$zid=daddslashes($_POST['zid']);
	$pwd=daddslashes($_POST['pwd']);
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"用户数据不存在"}');
	$sql = "UPDATE `pre_user` SET `pwd`='{$pwd}' WHERE zid='{$zid}'";
	if($DB->exec($sql)){
		exit('{"code":1,"msg":"修改成功","pwd":"'.$pwd.'"}');
	}else{
		exit('{"code":-1,"msg":"修改失败'.$DB->error().'"}');
	}	
break;
case 'delete_user':	//删除用户
	if($_POST['status'] == 'del'){
		$checkbox=$_POST['checkbox'];
		$DB->exec("DELETE FROM `pre_user` WHERE zid in(".implode(',',$checkbox).")");
		exit('{"code":1,"msg":"操作成功"}');
	}else{
		$zid=daddslashes($_POST['zid']);
		$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
		if(!$row)exit('{"code":-1,"msg":"用户数据不存在"}');
		$sql = "DELETE FROM `pre_user` WHERE zid='".$zid."'";
		if($DB->exec($sql)){
			exit('{"code":1,"msg":"删除成功"}');
		}else{
			exit('{"code":-1,"msg":"删除失败'.$DB->error().'"}');
		}		
	}
break;
case 'recharge_tc':
	$zid=daddslashes($_POST['zid']);
	$money=daddslashes($_POST['money']);
	$type=daddslashes($_POST['type']);
	$remark=isset($_POST['remark'])?daddslashes($_POST['remark']):'';
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"用户数据不存在"}');
	if($type == '1'){	//扣款
		$rmb = floatval($row['rmb_tc']) - floatval($money);
		$ret = '扣款';
	}else{
		$rmb = floatval($row['rmb_tc']) + floatval($money);
		$ret = '加款';
	}
	$ret2 = $ret.$money.'元';
	$ret2 = $remark==''?$ret2:$ret2.'【'.$remark.'】';
	$sql = "UPDATE pre_user SET rmb_tc='{$rmb}' WHERE zid='{$zid}'";
	if($DB->exec($sql)){
		add_money_log($zid,'佣金',$ret,$money,$row['rmb_tc'],$rmb,$ret2);
		exit('{"code":1,"msg":"成功'.$ret.'"}');
	}else{
		exit('{"code":-1,"msg":"失败'.$ret.$DB->error().'"}');
	}
break;
case 'recharge':	//用户加款
	$zid=daddslashes($_POST['zid']);
	$money=daddslashes($_POST['money']);
	$type=daddslashes($_POST['type']);
	$remark=isset($_POST['remark'])?daddslashes($_POST['remark']):'';
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
	if(!$row)exit('{"code":-1,"msg":"用户数据不存在"}');
	if($type == '1'){	//扣款
		$rmb = floatval($row['rmb']) - floatval($money);
		$ret = '站长扣款';
	}else{
		$rmb = floatval($row['rmb']) + floatval($money);
		$ret = '站长加款';
	}
	$ret2 = $ret.$money.'元';
	$ret2 = $remark==''?$ret2:$ret2.'【'.$remark.'】';
	$sql = "UPDATE pre_user SET rmb='{$rmb}' WHERE zid='{$zid}'";
	if($DB->exec($sql)){
		add_money_log($zid,'余额',$ret,$money,$row['rmb'],$rmb,$ret2);
		exit('{"code":1,"msg":"成功'.$ret.'"}');
	}else{
		exit('{"code":-1,"msg":"失败'.$ret.$DB->error().'"}');
	}
break;
case 'delete_qq':	//删除
	$biz = isset($_POST['biz']) ? daddslashes($_POST['biz']) : 'cloud';
	$biz = $biz === 'dg' ? 'dg' : 'cloud';
	$qq_table = $biz === 'dg' ? 'pre_dg_qq' : 'pre_qq';
	// 检查参数传递方式
	if(isset($_POST['status']) && $_POST['status'] == 'dle'){
		// 批量删除模式
		$checkbox=$_POST['checkbox'];
		$rs=$DB->query("SELECT qq FROM `{$qq_table}` WHERE id in(".implode(',',$checkbox).")");
		$deleted_count = 0;
		$failed_count = 0;
		while($res = $rs->fetch()){
			if($biz === 'cloud' ? set_qq_user($res['qq'],0,1) : ($DB->exec("DELETE FROM {$qq_table} WHERE qq='".daddslashes($res['qq'])."'")!==false)) {
				$deleted_count++;
			} else {
				$failed_count++;
			}
		}
		if($failed_count > 0) {
			exit('{"code":-1,"msg":"成功删除 '.$deleted_count.' 个QQ，失败 '.$failed_count.' 个"}');
		}
		exit('{"code":1,"msg":"成功删除 '.$deleted_count.' 个QQ"}');
	}else if(isset($_POST['id']) && $_POST['id'] > 0){
		// 单个删除模式（只有id参数）
		$id=intval($_POST['id']);
		$row = $DB->getRow("SELECT * FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
		if(!$row)exit('{"code":-1,"msg":"QQ账号不存在"}');
		
		// 执行删除操作
		$ok = $biz === 'cloud' ? set_qq_user($row['qq'],0,1) : ($DB->exec("DELETE FROM {$qq_table} WHERE id='{$id}'")!==false);
		if(!$ok){
			exit('{"code":-1,"msg":"删除操作执行失败"}');
		}
		
		// 验证删除是否成功
		$check = $DB->getRow("SELECT * FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
		if($check) {
			exit('{"code":-1,"msg":"删除失败：数据仍然存在"}');
		}
		
		exit('{"code":1,"msg":"成功删除QQ账号"}');
	}else if(isset($_POST['qq']) && $_POST['qq'] != ''){
		// 旧版删除模式（有qq参数）
		$id=isset($_POST['id'])?intval($_POST['id']):0;
		$qq=daddslashes($_POST['qq']);
		$type=isset($_POST['type'])?daddslashes($_POST['type']):1;	//1是删除全部
		
		if($id > 0){
			$row = $DB->getRow("SELECT * FROM `{$qq_table}` WHERE id='{$id}' LIMIT 1");
			if(!$row)exit('{"code":-1,"msg":"数据不存在"}');
		}
		
		// 执行删除操作
		if($biz === 'cloud'){
			if(!set_qq_user($qq,0,1)) {
				exit('{"code":-1,"msg":"删除操作执行失败"}');
			}
		}else{
			if($DB->exec("DELETE FROM {$qq_table} WHERE qq='{$qq}'")===false){
			exit('{"code":-1,"msg":"删除操作执行失败"}');
			}
		}
		
		// 验证删除是否成功
		$check = $DB->getRow("SELECT * FROM `{$qq_table}` WHERE qq='{$qq}' LIMIT 1");
		if($check) {
			exit('{"code":-1,"msg":"删除失败：数据仍然存在"}');
		}
		
		exit('{"code":1,"msg":"成功删除QQ账号"}');
	}else{
		exit('{"code":-1,"msg":"缺少必要参数"}');
	}
break;
case 'account':	//修改管理员账号密码
	$user=daddslashes($_POST['user']);
	$oldpwd=daddslashes($_POST['oldpwd']);
	$newpwd=daddslashes($_POST['newpwd']);
	$newpwd2=daddslashes($_POST['newpwd2']);
	$pwd = $conf['admin_pwd'];
	if($newpwd != '' || $newpwd2 != ''){
		if($oldpwd !=$pwd)exit('{"code":-1,"msg":"旧密码不正确"}');
		if($newpwd != $newpwd2){
			exit('{"code":-1,"msg":"输入的密码不配"}');
		}elseif(strlen($newpwd2) < 6) {
			exit('{"code":-1,"msg":"密码不能低于6位"}');
		}else{
			$pwd = $newpwd2;
		}
		$DB->exec("UPDATE pre_config SET v=:v WHERE k=:key", [':key'=>'admin_pwd',':v'=>$pwd]);
	}
	$DB->exec("UPDATE pre_config SET v=:v WHERE k=:key", [':key'=>'admin_user',':v'=>$user]);
	exit('{"code":1,"msg":"修改成功"}');
break;
case 'gonggao':	//公告设置
	$i=0;
	foreach($_POST as $k=>$v){
		$DB->exec("UPDATE pre_config SET v=:v WHERE k=:key", [':key'=>$k,':v'=>$v]);
		$i++;
	}
	if($i==0){
		exit('{"code":-1,"msg":"修改/保存失败"}');
	}else{
		exit('{"code":1,"msg":"修改/保存成功"}');
	}
break;
case 'agent':
	$zid=daddslashes($_POST['zid']);
	$row = $DB->getRow("SELECT * FROM `pre_user` WHERE zid='{$zid}' LIMIT 1");
	if($row['upzid_all']=='')exit('自己的直属用户查个毛线');
	$upzid_all = explode('_',$row['upzid_all']);
	$zidAll = array();
	foreach ($upzid_all as $value) {
		$zidAll[] = "'".$value."'";
	}
	$upzid_all = '';
	$agent = count($zidAll);
	$rs=$DB->query("SELECT zid,user FROM `pre_user` where zid in(".implode(',',$zidAll).") order by zid desc");//必须是desc降序
	while($res = $rs->fetch()){
		$upzid_all .= $agent.'级代理----账号='.$res['user'].'<br>';
		$agent--;
	}
	exit($upzid_all);
break;
case 'save_pay_interface':
	$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
	$pay_type = daddslashes($_POST['pay_type']);
	$interface_type = daddslashes($_POST['interface_type']);
	$name = daddslashes($_POST['name']);
	$status = intval($_POST['status']);
	$sort = intval($_POST['sort']);
	
	if(empty($pay_type) || empty($interface_type) || empty($name)){
		exit('{"code":-1,"msg":"请填写完整信息"}');
	}
	
	$config = isset($_POST['config']) ? $_POST['config'] : [];
	$config_json = json_encode($config);
	
	if($id > 0){
		$sql = "UPDATE `pre_pay_interface` SET pay_type='{$pay_type}', interface_type='{$interface_type}', name='{$name}', status='{$status}', sort='{$sort}', config='{$config_json}' WHERE id='{$id}'";
	}else{
		$sql = "INSERT INTO `pre_pay_interface` (pay_type, interface_type, name, status, sort, config) VALUES ('{$pay_type}', '{$interface_type}', '{$name}', '{$status}', '{$sort}', '{$config_json}')";
	}
	
	if($DB->exec($sql)){
		exit('{"code":1,"msg":"保存成功"}');
	}else{
		exit('{"code":-1,"msg":"保存失败"}');
	}
break;
case 'get_pay_interface':
	$id = intval($_GET['id']);
	$row = $DB->getRow("SELECT * FROM `pre_pay_interface` WHERE id='{$id}' LIMIT 1");
	if($row){
		exit('{"code":1,"msg":"获取成功","data":'.json_encode($row).'}');
	}else{
		exit('{"code":-1,"msg":"接口不存在"}');
	}
break;
case 'delete_pay_interface':
	$id = intval($_POST['id']);
	if($DB->exec("DELETE FROM `pre_pay_interface` WHERE id='{$id}'")){
		exit('{"code":1,"msg":"删除成功"}');
	}else{
		exit('{"code":-1,"msg":"删除失败"}');
	}
break;
case 'fix_schema':
	// 自动修复缺失的表和列
	$fix_results = [];
	
	// 定义完整建表SQL
	$create_table_sql = [
		'pre_qq' => "CREATE TABLE IF NOT EXISTS `pre_qq` (
			`id` int(11) NOT NULL AUTO_INCREMENT,
			`qq` varchar(20) NOT NULL,
			`pwd` varchar(255) NOT NULL,
			`zid` int(11) DEFAULT 0,
			`cityid` int(11) DEFAULT 0,
			`endtime_arr` text,
			`vip_time` datetime DEFAULT NULL,
			`addtime` datetime DEFAULT CURRENT_TIMESTAMP,
			`login_zt` tinyint(1) DEFAULT 0,
			`status` tinyint(1) DEFAULT 1,
			`remark` varchar(255) DEFAULT '',
			`token` varchar(64) DEFAULT '',
			PRIMARY KEY (`id`),
			KEY `idx_qq` (`qq`),
			KEY `idx_zid` (`zid`)
		) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
		'pre_dg_qq' => "CREATE TABLE IF NOT EXISTS `pre_dg_qq` (
			`id` int(11) NOT NULL AUTO_INCREMENT,
			`qq` varchar(20) NOT NULL,
			`pwd` varchar(255) NOT NULL,
			`zid` int(11) DEFAULT 0,
			`cityid` int(11) DEFAULT 0,
			`endtime_arr` text,
			`vip_time` datetime DEFAULT NULL,
			`addtime` datetime DEFAULT CURRENT_TIMESTAMP,
			`login_zt` tinyint(1) DEFAULT 0,
			`status` tinyint(1) DEFAULT 1,
			`remark` varchar(255) DEFAULT '',
			`token` varchar(64) DEFAULT '',
			PRIMARY KEY (`id`),
			KEY `idx_qq` (`qq`)
		) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
		'pre_qzone_qq' => "CREATE TABLE IF NOT EXISTS `pre_qzone_qq` (
			`id` int(11) NOT NULL AUTO_INCREMENT,
			`qq` varchar(20) NOT NULL,
			`pwd` varchar(255) NOT NULL,
			`zid` int(11) DEFAULT 0,
			`vip_time` datetime DEFAULT NULL,
			`addtime` datetime DEFAULT CURRENT_TIMESTAMP,
			`status` tinyint(1) DEFAULT 1,
			`remark` varchar(255) DEFAULT '',
			`token` varchar(64) DEFAULT '',
			`cookie_status` tinyint(1) DEFAULT 0,
			PRIMARY KEY (`id`),
			KEY `idx_qq` (`qq`)
		) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
	];
	
	// 添加列的SQL
	$add_column_sql = [
		'pre_qq' => [
			'token' => "ALTER TABLE `pre_qq` ADD COLUMN `token` varchar(64) DEFAULT '' AFTER `remark`"
		],
		'pre_dg_qq' => [
			'token' => "ALTER TABLE `pre_dg_qq` ADD COLUMN `token` varchar(64) DEFAULT '' AFTER `remark`"
		],
		'pre_qzone_qq' => [
			'token' => "ALTER TABLE `pre_qzone_qq` ADD COLUMN `token` varchar(64) DEFAULT '' AFTER `remark`",
			'cookie_status' => "ALTER TABLE `pre_qzone_qq` ADD COLUMN `cookie_status` tinyint(1) DEFAULT 0 AFTER `token`"
		]
	];
	
	// 先检测当前状态
	$expected_schema = [
		'pre_qq' => ['id', 'qq', 'pwd', 'zid', 'cityid', 'endtime_arr', 'vip_time', 'addtime', 'login_zt', 'status', 'remark', 'token'],
		'pre_dg_qq' => ['id', 'qq', 'pwd', 'zid', 'cityid', 'endtime_arr', 'vip_time', 'addtime', 'login_zt', 'status', 'remark', 'token'],
		'pre_qzone_qq' => ['id', 'qq', 'pwd', 'zid', 'vip_time', 'addtime', 'status', 'remark', 'token', 'cookie_status']
	];
	
	$fixed_count = 0;
	$error_msg = '';
	
	foreach($expected_schema as $table => $expected_columns) {
		// 检查表是否存在
		$table_exists = $DB->getColumn("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='{$table}'");
		
		if(!$table_exists) {
			// 创建表
			if(isset($create_table_sql[$table])) {
				if($DB->exec($create_table_sql[$table]) !== false) {
					$fix_results[] = "创建表 {$table} 成功";
					$fixed_count++;
				} else {
					$error_msg .= "创建表 {$table} 失败; ";
				}
			}
			continue;
		}
		
		// 获取现有列
		$existing_columns = [];
		$rs = $DB->query("SHOW COLUMNS FROM {$table}");
		while($row = $rs->fetch(PDO::FETCH_ASSOC)) {
			$existing_columns[] = $row['Field'];
		}
		
		// 检查缺失的列
		foreach($expected_columns as $col) {
			if(!in_array($col, $existing_columns)) {
				if(isset($add_column_sql[$table][$col])) {
					if($DB->exec($add_column_sql[$table][$col]) !== false) {
						$fix_results[] = "表 {$table} 添加列 {$col} 成功";
						$fixed_count++;
					} else {
						$error_msg .= "表 {$table} 添加列 {$col} 失败; ";
					}
				}
			}
		}
	}
	
	if($fixed_count > 0) {
		exit(json_encode([
			'code' => 1,
			'msg' => "修复完成，共修复 {$fixed_count} 项",
			'data' => ['details' => $fix_results, 'errors' => $error_msg]
		]));
	} else {
		exit(json_encode([
			'code' => 0,
			'msg' => '无需修复，数据库结构正常',
			'data' => ['details' => $fix_results]
		]));
	}
break;
 
case 'check_update':
	// 获取当前版本
	$auth_config = include ROOT . 'includes/Auth.php';
	$current_version = $auth_config['app_version'] ?? '1.0';
	
	// 调用授权站API检测更新
	$api_url = 'https://sqz.dadxb.cn/api/check-update.php?current_version=' . urlencode($current_version);
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $api_url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_TIMEOUT, 15);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	$response = curl_exec($ch);
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);
	
	if($http_code == 200 && $response) {
		$result = json_decode($response, true);
		if($result && isset($result['success']) && $result['success']) {
			$data = $result['data'];
			if($data['has_update']) {
				exit(json_encode([
					'code' => 1,
					'msg' => '发现新版本',
					'data' => [
						'version' => $data['latest_version'],
						'appname' => $data['name'] ?? '小韭菜',
						'date' => $data['release_date'] ?? '',
						'content' => $data['changelog'] ?? '',
						'download_url' => $data['download_url'] ?? '',
						'file_size' => $data['file_size'] ?? 0
					]
				]));
			} else {
				exit(json_encode([
					'code' => 0,
					'msg' => '当前已是最新版本',
					'data' => [
						'version' => $current_version
					]
				]));
			}
		} else {
			exit(json_encode(['code' => -1, 'msg' => $result['message'] ?? '检测失败']));
		}
	} else {
		exit(json_encode(['code' => -1, 'msg' => '无法连接授权服务器']));
	}
break;
case 'download_update':
	// 步骤1: 下载更新包
	$download_url = isset($_POST['download_url']) ? trim($_POST['download_url']) : '';
	$new_version = isset($_POST['new_version']) ? trim($_POST['new_version']) : '';
	
	if(empty($download_url)) {
		exit(json_encode(['code' => -1, 'msg' => '下载地址为空']));
	}
	
	// 创建临时目录
	$temp_dir = ROOT . 'cache/update_temp/';
	if(!is_dir(ROOT . 'cache')) {
		mkdir(ROOT . 'cache', 0777, true);
	}
	if(!is_dir($temp_dir)) {
		mkdir($temp_dir, 0777, true);
	}
	
	// 下载更新包
	$zip_file = $temp_dir . 'update.zip';
	$fp = fopen($zip_file, 'w+');
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $download_url);
	curl_setopt($ch, CURLOPT_FILE, $fp);
	curl_setopt($ch, CURLOPT_TIMEOUT, 300);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
	$download_result = curl_exec($ch);
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	$error = curl_error($ch);
	curl_close($ch);
	fclose($fp);
	
	if(!$download_result || $http_code != 200 || !file_exists($zip_file) || filesize($zip_file) < 1000) {
		if(file_exists($zip_file)) unlink($zip_file);
		exit(json_encode(['code' => -1, 'msg' => '下载失败 (HTTP ' . $http_code . ($error ? ': ' . $error : '') . ')']));
	}
	
	exit(json_encode([
		'code' => 1,
		'msg' => '下载成功',
		'data' => [
			'zip_file' => $zip_file,
			'file_size' => filesize($zip_file),
			'new_version' => $new_version
		]
	]));
break;
case 'extract_update':
	// 步骤2: 解压更新包
	$temp_dir = ROOT . 'cache/update_temp/';
	$zip_file = $temp_dir . 'update.zip';
	
	if(!file_exists($zip_file)) {
		exit(json_encode(['code' => -1, 'msg' => '更新包不存在，请重新下载']));
	}
	
	$zip = new ZipArchive;
	if($zip->open($zip_file) !== TRUE) {
		unlink($zip_file);
		exit(json_encode(['code' => -1, 'msg' => '无法打开更新包，文件可能已损坏']));
	}
	
	// 获取文件总数
	$file_count = $zip->numFiles;
	
	// 解压到临时目录
	$extract_result = $zip->extractTo($temp_dir);
	$zip->close();
	
	if(!$extract_result) {
		exit(json_encode(['code' => -1, 'msg' => '解压失败']));
	}
	
	// 查找解压后的目录
	$source_dir = $temp_dir;
	$dirs = glob($temp_dir . '*', GLOB_ONLYDIR);
	if($dirs && count($dirs) == 1 && basename($dirs[0]) !== 'backup') {
		$source_dir = $dirs[0];
	}
	
	exit(json_encode([
		'code' => 1,
		'msg' => '解压成功',
		'data' => [
			'source_dir' => $source_dir,
			'file_count' => $file_count
		]
	]));
break;
case 'install_update':
	// 步骤3: 安装更新
	$temp_dir = ROOT . 'cache/update_temp/';
	$new_version = isset($_POST['new_version']) ? trim($_POST['new_version']) : '';
	
	// 查找源目录
	$source_dir = $temp_dir;
	$dirs = glob($temp_dir . '*', GLOB_ONLYDIR);
	if($dirs && count($dirs) == 1 && basename($dirs[0]) !== 'backup') {
		$source_dir = $dirs[0];
	}
	
	if(!is_dir($source_dir)) {
		exit(json_encode(['code' => -1, 'msg' => '源目录不存在']));
	}
	
	// 备份关键文件
	$backup_dir = ROOT . 'cache/backup_' . time() . '/';
	mkdir($backup_dir, 0777, true);
	
	$backup_files = [
		'config.php',
		'includes/Auth_Local_License.key',
		'install/install.lock'
	];
	
	foreach($backup_files as $file) {
		if(file_exists(ROOT . $file)) {
			$backup_path = $backup_dir . dirname($file);
			if(!is_dir($backup_path)) {
				mkdir($backup_path, 0777, true);
			}
			copy(ROOT . $file, $backup_dir . $file);
		}
	}
	
	// 统计文件数
	$file_iterator = new RecursiveIteratorIterator(
		new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS),
		RecursiveIteratorIterator::LEAVES_ONLY
	);
	$total_files = iterator_count($file_iterator);
	
	// 复制文件
	$files_copied = 0;
	$files_failed = 0;
	$processed = 0;
	
	$iterator = new RecursiveIteratorIterator(
		new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS),
		RecursiveIteratorIterator::SELF_FIRST
	);
	
	foreach($iterator as $item) {
		$relative_path = str_replace($source_dir . '/', '', $item->getPathname());
		$target_path = ROOT . $relative_path;
		
		if($item->isDir()) {
			if(!is_dir($target_path)) {
				mkdir($target_path, 0777, true);
			}
		} else {
			// 跳过配置文件和锁定文件
			if(in_array($relative_path, ['config.php', 'includes/Auth_Local_License.key', 'install/install.lock'])) {
				continue;
			}
			
			$target_dir = dirname($target_path);
			if(!is_dir($target_dir)) {
				mkdir($target_dir, 0777, true);
			}
			
			if(copy($item->getPathname(), $target_path)) {
				$files_copied++;
			} else {
				$files_failed++;
			}
		}
	}
	
	// 清理临时文件
	@unlink($temp_dir . 'update.zip');
	
	// 更新版本号
	if($new_version) {
		$DB->query("REPLACE INTO pre_config SET v='" . daddslashes($new_version) . "',k='version'");
		
		// 更新 Auth.php 版本号
		$auth_file = ROOT . 'includes/Auth.php';
		if(file_exists($auth_file)) {
			$auth_content = file_get_contents($auth_file);
			$auth_content = preg_replace(
				"/'app_version'\s*=>\s*'[^']*'/",
				"'app_version' => '" . $new_version . "'",
				$auth_content
			);
			file_put_contents($auth_file, $auth_content);
		}
	}
	
	exit(json_encode([
		'code' => 1,
		'msg' => '安装成功',
		'data' => [
			'version' => $new_version,
			'files_copied' => $files_copied,
			'files_failed' => $files_failed,
			'backup_dir' => $backup_dir,
			'total_files' => $total_files
		]
	]));
break;
case 'do_update':
	// 保留原接口兼容性，但使用新的分步方式
	// 获取当前版本
	$auth_config = include ROOT . 'includes/Auth.php';
	$current_version = $auth_config['app_version'] ?? '1.0';
	
	// 先检测更新
	$check_api_url = 'https://sqz.dadxb.cn/api/check-update.php?current_version=' . urlencode($current_version);
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $check_api_url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_TIMEOUT, 15);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	$check_response = curl_exec($ch);
	curl_close($ch);
	
	$check_result = json_decode($check_response, true);
	if(!$check_result || !$check_result['success'] || !$check_result['data']['has_update']) {
		exit(json_encode(['code' => 0, 'msg' => '当前已是最新版本，无需更新']));
	}
	
	$download_url = $check_result['data']['download_url'] ?? '';
	$new_version = $check_result['data']['latest_version'] ?? '';
	
	if(empty($download_url)) {
		exit(json_encode(['code' => -1, 'msg' => '未获取到下载地址']));
	}
	
	// 创建临时目录
	$temp_dir = ROOT . 'cache/update_' . time() . '/';
	if(!is_dir(ROOT . 'cache')) {
		mkdir(ROOT . 'cache', 0777, true);
	}
	if(!is_dir($temp_dir)) {
		mkdir($temp_dir, 0777, true);
	}
	
	// 下载更新包
	$zip_file = $temp_dir . 'update.zip';
	$fp = fopen($zip_file, 'w+');
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $download_url);
	curl_setopt($ch, CURLOPT_FILE, $fp);
	curl_setopt($ch, CURLOPT_TIMEOUT, 300);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
	$download_result = curl_exec($ch);
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);
	fclose($fp);
	
	if(!$download_result || $http_code != 200 || !file_exists($zip_file) || filesize($zip_file) < 1000) {
		// 清理临时文件
		if(file_exists($zip_file)) unlink($zip_file);
		if(is_dir($temp_dir)) rmdir($temp_dir);
		exit(json_encode(['code' => -1, 'msg' => '下载更新包失败 (HTTP ' . $http_code . ')']));
	}
	
	// 解压更新包
	$zip = new ZipArchive;
	if($zip->open($zip_file) !== TRUE) {
		unlink($zip_file);
		exit(json_encode(['code' => -1, 'msg' => '无法打开更新包']));
	}
	
	// 解压到临时目录
	$extract_result = $zip->extractTo($temp_dir);
	$zip->close();
	
	if(!$extract_result) {
		// 清理
		self::deleteDir($temp_dir);
		exit(json_encode(['code' => -1, 'msg' => '解压更新包失败']));
	}
	
	// 查找解压后的目录（可能是根目录或包含子目录）
	$source_dir = $temp_dir;
	$dirs = glob($temp_dir . '*', GLOB_ONLYDIR);
	if($dirs && count($dirs) == 1) {
		// 如果只有一个子目录，使用该目录
		$source_dir = $dirs[0];
	}
	
	// 备份关键文件
	$backup_dir = ROOT . 'cache/backup_' . time() . '/';
	mkdir($backup_dir, 0777, true);
	
	$backup_files = [
		'config.php',
		'includes/Auth_Local_License.key',
		'install/install.lock'
	];
	
	foreach($backup_files as $file) {
		if(file_exists(ROOT . $file)) {
			$backup_path = $backup_dir . dirname($file);
			if(!is_dir($backup_path)) {
				mkdir($backup_path, 0777, true);
			}
			copy(ROOT . $file, $backup_dir . $file);
		}
	}
	
	// 复制文件到根目录
	$files_copied = 0;
	$files_failed = 0;
	
	$iterator = new RecursiveIteratorIterator(
		new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS),
		RecursiveIteratorIterator::SELF_FIRST
	);
	
	foreach($iterator as $item) {
		$relative_path = str_replace($source_dir . '/', '', $item->getPathname());
		$target_path = ROOT . $relative_path;
		
		if($item->isDir()) {
			if(!is_dir($target_path)) {
				mkdir($target_path, 0777, true);
			}
		} else {
			// 跳过配置文件
			if(in_array($relative_path, ['config.php', 'includes/Auth_Local_License.key', 'install/install.lock'])) {
				continue;
			}
			
			$target_dir = dirname($target_path);
			if(!is_dir($target_dir)) {
				mkdir($target_dir, 0777, true);
			}
			
			if(copy($item->getPathname(), $target_path)) {
				$files_copied++;
			} else {
				$files_failed++;
			}
		}
	}
	
	// 清理临时文件
	function deleteUpdateDir($dir) {
		if(!is_dir($dir)) return;
		$items = new RecursiveIteratorIterator(
			new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
			RecursiveIteratorIterator::CHILD_FIRST
		);
		foreach($items as $item) {
			if($item->isDir()) {
				rmdir($item->getPathname());
			} else {
				unlink($item->getPathname());
			}
		}
		rmdir($dir);
	}
	deleteUpdateDir($temp_dir);
	
	// 更新版本号到数据库
	$DB->query("REPLACE INTO pre_config SET v='" . daddslashes($new_version) . "',k='version'");
	
	// 更新 Auth.php 版本号
	$auth_file = ROOT . 'includes/Auth.php';
	if(file_exists($auth_file)) {
		$auth_content = file_get_contents($auth_file);
		$auth_content = preg_replace(
			"/'app_version'\s*=>\s*'[^']*'/",
			"'app_version' => '" . $new_version . "'",
			$auth_content
		);
		file_put_contents($auth_file, $auth_content);
	}
	
	exit(json_encode([
		'code' => 1,
		'msg' => '更新成功',
		'data' => [
			'version' => $new_version,
			'files_copied' => $files_copied,
			'files_failed' => $files_failed,
			'backup_dir' => $backup_dir
		]
	]));
break;
case 'check_auth_status':
	$result = auth_status();
	exit(json_encode($result));
break;
case 'get_version_list':
	$result = get_version_list();
	exit(json_encode($result));
break;
case 'get_system_switches':
	$cloud_enabled = $conf['cloud_enabled'] ?? '1';
	$qzone_open = $conf['qzone_open'] ?? ($conf['qzone_enabled'] ?? '0');
	$dg_open = $conf['dg_open'] ?? '0';
	exit(json_encode([
		'code' => 1,
		'msg' => '获取成功',
		'data' => [
			'cloud_enabled' => $cloud_enabled,
			'qzone_open' => $qzone_open,
			'dg_open' => $dg_open
		]
	]));
break;
case 'save_system_switch':
	$name = isset($_POST['name']) ? trim($_POST['name']) : '';
	$value = isset($_POST['value']) ? trim($_POST['value']) : '';
	
	if (!in_array($name, ['cloud_enabled', 'qzone_open', 'dg_open'])) {
		exit(json_encode(['code' => -1, 'msg' => '无效的配置项']));
	}
	
	if (!in_array($value, ['0', '1'])) {
		exit(json_encode(['code' => -1, 'msg' => '无效的配置值']));
	}
	
	// 使用 REPLACE INTO 语法，与 set case 保持一致
	$name = daddslashes($name);
	$value = daddslashes($value);
	$DB->query("REPLACE INTO pre_config SET v='{$value}',k='{$name}'");
	
	exit(json_encode(['code' => 1, 'msg' => '保存成功']));
break;
// ========== 跨站节点授权管理 ==========
case 'node_auth_list':
	$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
	$search = isset($_GET['search']) ? trim($_GET['search']) : '';
	$pageSize = 20;
	$offset = ($page - 1) * $pageSize;
	
	// 构建搜索条件
	$whereClause = '';
	$params = [];
	if ($search) {
		$whereClause = " WHERE a.contact_qq LIKE ? OR a.site_name LIKE ? OR a.site_url LIKE ?";
		$searchParam = '%' . $search . '%';
		$params = [$searchParam, $searchParam, $searchParam];
	}
	
	$total = $DB->getColumn("SELECT count(*) FROM pre_node_auth a{$whereClause}", $params);
	$totalPages = ceil($total / $pageSize);
	
	// 获取列表，关联节点表获取节点名称
	$sql = "SELECT a.*, 
		CASE 
			WHEN a.auth_type = 1 THEN '全站节点'
			WHEN a.auth_type = 2 THEN (SELECT name FROM pre_login_class WHERE id = a.class_id)
			WHEN a.auth_type = 3 THEN (SELECT citybz FROM pre_loginconfig WHERE id = a.node_id)
		END as node_name
		FROM pre_node_auth a {$whereClause} ORDER BY a.id DESC LIMIT {$offset}, {$pageSize}";
	
	$rs = $DB->query($sql, $params);
	$list = [];
	while ($row = $rs->fetch(PDO::FETCH_ASSOC)) {
		$list[] = $row;
	}
	
	exit(json_encode([
		'code' => 0,
		'msg' => 'success',
		'data' => [
			'list' => $list,
			'total' => (int)$total,
			'totalPages' => (int)$totalPages
		]
	]));
break;

case 'node_auth_toggle_status':
	$id = intval($_POST['id']);
	$status = intval($_POST['status']);
	
	if ($id <= 0) {
		exit('{"code":-1,"msg":"参数错误"}');
	}
	
	$DB->exec("UPDATE pre_node_auth SET status = ? WHERE id = ?", [$status, $id]);
	exit(json_encode(['code' => 0, 'msg' => '状态已更新']));
break;

case 'node_auth_adjust_expire':
	$id = intval($_POST['id']);
	$action = trim($_POST['action']);
	
	if ($id <= 0) {
		exit('{"code":-1,"msg":"参数错误"}');
	}
	
	$auth = $DB->getRow("SELECT * FROM pre_node_auth WHERE id = ?", [$id]);
	if (!$auth) {
		exit('{"code":-1,"msg":"授权不存在"}');
	}
	
	$newExpireTime = null;
	
	if ($action === 'set') {
		// 设置为永久
		$newExpireTime = null;
	} elseif ($action === 'custom') {
		// 自定义时间
		$customTime = trim($_POST['custom_time']);
		if ($customTime) {
			$newExpireTime = $customTime . ':00';
		}
	} elseif ($action === 'add' || $action === 'subtract') {
		$amount = intval($_POST['amount']);
		$unit = trim($_POST['unit']);
		
		if ($amount <= 0) {
			exit('{"code":-1,"msg":"请输入有效的时长数量"}');
		}
		
		// 获取当前到期时间，如果没有则从现在开始
		$baseTime = $auth['expire_time'] ? strtotime($auth['expire_time']) : time();
		
		// 如果已过期且是增加时长，从现在开始计算
		if ($action === 'add' && $baseTime < time()) {
			$baseTime = time();
		}
		
		// 计算时间增量
		$seconds = 0;
		switch ($unit) {
			case 'day': $seconds = $amount * 86400; break;
			case 'month': $seconds = $amount * 86400 * 30; break;
			case 'year': $seconds = $amount * 86400 * 365; break;
		}
		
		if ($action === 'add') {
			$newExpireTime = date('Y-m-d H:i:s', $baseTime + $seconds);
		} else {
			$newExpireTime = date('Y-m-d H:i:s', $baseTime - $seconds);
		}
	}
	
	$DB->exec("UPDATE pre_node_auth SET expire_time = ? WHERE id = ?", [$newExpireTime, $id]);
	exit(json_encode(['code' => 0, 'msg' => '到期时间已更新']));
break;

case 'node_auth_save':
	$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
	$auth_type = intval($_POST['auth_type']);
	$class_id = isset($_POST['class_id']) ? intval($_POST['class_id']) : null;
	$node_id = isset($_POST['node_id']) ? intval($_POST['node_id']) : null;
	$app_id = trim($_POST['app_id']);
	$app_secret = trim($_POST['app_secret']);
	$site_name = trim($_POST['site_name']);
	$site_url = trim($_POST['site_url']);
	$contact_qq = trim($_POST['contact_qq']);
	$expire_time = trim($_POST['expire_time']);
	$status = intval($_POST['status']);
	$remark = trim($_POST['remark']);
	
	if (empty($app_id) || empty($app_secret)) {
		exit('{"code":-1,"msg":"App ID和App Secret不能为空"}');
	}
	
	if ($auth_type == 2 && !$class_id) {
		exit('{"code":-1,"msg":"请选择分类"}');
	}
	
	if ($auth_type == 3 && !$node_id) {
		exit('{"code":-1,"msg":"请选择节点"}');
	}
	
	$expire_time = $expire_time ? $expire_time . ':00' : null;
	
	if ($id > 0) {
		// 更新
		$sql = "UPDATE pre_node_auth SET auth_type=?, class_id=?, node_id=?, site_name=?, site_url=?, contact_qq=?, expire_time=?, status=?, remark=? WHERE id=?";
		$params = [$auth_type, $class_id ?: null, $node_id ?: null, $site_name, $site_url, $contact_qq, $expire_time, $status, $remark, $id];
		if ($DB->exec($sql, $params)) {
			exit('{"code":0,"msg":"修改成功"}');
		} else {
			exit('{"code":-1,"msg":"修改失败"}');
		}
	} else {
		// 检查app_id是否重复
		$exists = $DB->getColumn("SELECT count(*) FROM pre_node_auth WHERE app_id=?", [$app_id]);
		if ($exists > 0) {
			exit('{"code":-1,"msg":"App ID已存在"}');
		}
		
		// 新增
		$sql = "INSERT INTO pre_node_auth (app_id, app_secret, auth_type, class_id, node_id, site_name, site_url, contact_qq, expire_time, status, remark, addtime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
		$params = [$app_id, $app_secret, $auth_type, $class_id ?: null, $node_id ?: null, $site_name, $site_url, $contact_qq, $expire_time, $status, $remark];
		if ($DB->exec($sql, $params)) {
			exit('{"code":0,"msg":"添加成功"}');
		} else {
			exit('{"code":-1,"msg":"添加失败"}');
		}
	}
break;

case 'node_auth_delete':
	$id = intval($_POST['id']);
	if ($DB->exec("DELETE FROM pre_node_auth WHERE id=?", [$id])) {
		exit('{"code":0,"msg":"删除成功"}');
	} else {
		exit('{"code":-1,"msg":"删除失败"}');
	}
break;

// ========== 对接方节点管理 ==========
case 'node_link_credential_list':
	$biz = isset($_GET['biz']) ? trim($_GET['biz']) : 'all';
	if ($biz === 'cloud' || $biz === 'dg') {
		$b = addslashes($biz);
		$whereBiz = " WHERE biz = '{$b}'";
	} else {
		$whereBiz = '';
	}
	
	// 获取凭证列表（按app_id分组）
	$rs = $DB->query("SELECT app_id, api_url, biz, MAX(expire_time) as expire_time, COUNT(*) as node_count FROM pre_node_link {$whereBiz} GROUP BY app_id, api_url, biz");
	$list = [];
	while ($row = $rs->fetch(PDO::FETCH_ASSOC)) {
		// 获取该凭证下所有节点名称（与分组维度一致，避免同 app_id 不同业务串名）
		$nodeNames = $DB->getColumn(
			"SELECT GROUP_CONCAT(IFNULL(custom_name, remote_class_name) SEPARATOR ', ') FROM pre_node_link WHERE app_id = ? AND api_url = ? AND biz = ?",
			[$row['app_id'], $row['api_url'], $row['biz']]
		);
		$row['node_names'] = $nodeNames ?: '';
		$list[] = $row;
	}
	exit(json_encode(['code' => 0, 'msg' => 'success', 'data' => $list]));
break;

case 'node_link_list':
	$biz = isset($_GET['biz']) ? trim($_GET['biz']) : 'all';
	if ($biz === 'cloud' || $biz === 'dg') {
		$b = addslashes($biz);
		$whereBiz = " WHERE n.biz = '{$b}'";
	} else {
		$whereBiz = '';
	}
	
	// 获取对接节点列表
	// 不再获取/拼接本地分类名称（仅返回 local_class_id 由管理员手工维护）
	$rs = $DB->query("SELECT n.* FROM pre_node_link n {$whereBiz} ORDER BY n.sort ASC, n.id ASC");
	$list = [];
	while ($row = $rs->fetch(PDO::FETCH_ASSOC)) {
		$list[] = $row;
	}
	exit(json_encode(['code' => 0, 'msg' => 'success', 'data' => $list]));
break;

case 'node_link_toggle_status':
	$id = intval($_POST['id']);
	$status = intval($_POST['status']);
	
	if ($id <= 0) {
		exit('{"code":-1,"msg":"参数错误"}');
	}
	
	$DB->exec("UPDATE pre_node_link SET status = ? WHERE id = ?", [$status, $id]);
	exit(json_encode(['code' => 0, 'msg' => '状态已更新']));
break;

case 'node_link_add_credential':
	$api_url = trim($_POST['api_url']);
	$app_id = trim($_POST['app_id']);
	$app_secret = trim($_POST['app_secret']);
	$biz = isset($_POST['biz']) ? trim($_POST['biz']) : 'cloud';
	if (!in_array($biz, ['cloud', 'dg', 'both'], true)) {
		$biz = 'cloud';
	}
	
	if (empty($api_url) || empty($app_id) || empty($app_secret)) {
		exit('{"code":-1,"msg":"参数不完整"}');
	}
	
	// 测试连接
	$params = [
		'app_id' => $app_id,
		'timestamp' => time()
	];
	$signStr = 'app_id=' . $app_id . '&timestamp=' . $params['timestamp'] . '&key=' . $app_secret;
	$params['sign'] = strtoupper(md5($signStr));
	
	$ch = curl_init();
	curl_setopt_array($ch, [
		CURLOPT_URL => $api_url . '?action=list',
		CURLOPT_POST => true,
		CURLOPT_POSTFIELDS => http_build_query($params),
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_TIMEOUT => 10,
		CURLOPT_SSL_VERIFYPEER => false
	]);
	$response = curl_exec($ch);
	$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);
	
	if ($httpCode != 200) {
		exit('{"code":-1,"msg":"连接失败，请检查API地址是否正确"}');
	}
	
	$result = json_decode($response, true);
	if (!$result || $result['code'] != 0) {
		exit('{"code":-1,"msg":"' . ($result['msg'] ?? '验证失败') . '"}');
	}
	
	// 保存凭证信息到数据库（暂存，等拉取节点时使用）
	exit(json_encode(['code' => 0, 'msg' => '凭证验证成功', 'data' => ['api_url' => $api_url, 'app_id' => $app_id, 'app_secret' => $app_secret, 'biz' => $biz]]));
break;

case 'node_link_pull':
	$app_id = trim($_POST['app_id'] ?? '');
	$app_secret = trim($_POST['app_secret'] ?? '');
	$api_url = trim($_POST['api_url'] ?? '');
	$bizPost = isset($_POST['biz']) ? trim((string)$_POST['biz']) : '';
	// 全部：按被对接站每个节点自身的 biz 各写一条（不翻倍）；云端/代挂：只导入对应业务的节点
	$pullAllFromRemote = ($bizPost === 'both' || $bizPost === 'all');
	$bizFilter = in_array($bizPost, ['cloud', 'dg'], true) ? $bizPost : null;
	$local_class_id = intval($_POST['local_class_id'] ?? 0);

	// 拉取云端/代挂全部时，本地分类必须按业务分别设置；当前入口只支持填 0（自动同步）
	if (($bizPost === 'both' || $bizPost === 'all') && $local_class_id > 0) {
		exit('{"code":-1,"msg":"拉取云端/代挂全部时，local_class_id 必须填 0（自动同步）。请在节点列表中分别编辑云端/代挂的本地分类ID"}');
	}

	// 拉取单业务时，校验 local_class_id 属于该业务类型分类
	if ($local_class_id > 0 && $bizFilter !== null) {
		$loginClass = $DB->getRow("SELECT id,biz FROM pre_login_class WHERE id = ? LIMIT 1", [$local_class_id]);
		if (!$loginClass) {
			exit('{"code":-1,"msg":"本地分类ID不存在"}');
		}
		if (($loginClass['biz'] ?? '') !== $bizFilter) {
			exit('{"code":-1,"msg":"本地分类ID必须属于当前业务类型：'. $bizFilter .'"}');
		}
	}

	if ($app_id === '') {
		exit('{"code":-1,"msg":"缺少 app_id"}');
	}

	$baseCred = null;
	$importFilter = null;

	if ($pullAllFromRemote) {
		$cred = null;
		if ($api_url !== '') {
			$cred = $DB->getRow(
				"SELECT app_id, app_secret, api_url FROM pre_node_link WHERE app_id = ? AND api_url = ? ORDER BY id DESC LIMIT 1",
				[$app_id, $api_url]
			);
		}
		if (!$cred) {
			$cred = $DB->getRow(
				"SELECT app_id, app_secret, api_url FROM pre_node_link WHERE app_id = ? ORDER BY id DESC LIMIT 1",
				[$app_id]
			);
		}
		if (!$cred) {
			if ($app_secret === '' || $api_url === '') {
				exit('{"code":-1,"msg":"缺少凭证参数"}');
			}
			$baseCred = ['app_id' => $app_id, 'app_secret' => $app_secret, 'api_url' => $api_url];
		} else {
			$baseCred = ['app_id' => $cred['app_id'], 'app_secret' => $cred['app_secret'], 'api_url' => $cred['api_url']];
		}
		$importFilter = null;
	} else {
		$cred = null;
		if ($api_url !== '' && $bizFilter !== null) {
			$cred = $DB->getRow(
				"SELECT app_id, app_secret, api_url, biz FROM pre_node_link WHERE app_id = ? AND api_url = ? AND biz = ? LIMIT 1",
				[$app_id, $api_url, $bizFilter]
			);
		}
		if (!$cred && $bizFilter !== null) {
			$cred = $DB->getRow(
				"SELECT app_id, app_secret, api_url, biz FROM pre_node_link WHERE app_id = ? AND biz = ? ORDER BY id DESC LIMIT 1",
				[$app_id, $bizFilter]
			);
		}
		if (!$cred && $api_url !== '') {
			$cred = $DB->getRow(
				"SELECT app_id, app_secret, api_url, biz FROM pre_node_link WHERE app_id = ? AND api_url = ? ORDER BY id DESC LIMIT 1",
				[$app_id, $api_url]
			);
		}
		if (!$cred) {
			$cred = $DB->getRow(
				"SELECT app_id, app_secret, api_url, biz FROM pre_node_link WHERE app_id = ? ORDER BY id DESC LIMIT 1",
				[$app_id]
			);
		}
		if (!$cred) {
			if ($app_secret === '' || $api_url === '') {
				exit('{"code":-1,"msg":"缺少凭证参数"}');
			}
			$bizNew = $bizFilter !== null ? $bizFilter : 'cloud';
			$cred = ['app_id' => $app_id, 'app_secret' => $app_secret, 'api_url' => $api_url, 'biz' => $bizNew];
		}
		$credBizRaw = trim((string)($cred['biz'] ?? ''));
		if (!in_array($credBizRaw, ['cloud', 'dg'], true)) {
			$credBizRaw = $bizFilter !== null ? $bizFilter : 'cloud';
		}
		$baseCred = ['app_id' => $cred['app_id'], 'app_secret' => $cred['app_secret'], 'api_url' => $cred['api_url']];
		$importFilter = $credBizRaw;
	}

	$params = [
		'app_id' => $baseCred['app_id'],
		'timestamp' => time(),
	];
	ksort($params);
	$signStr = '';
	foreach ($params as $k => $v) {
		if ($k !== 'sign' && $v !== '') {
			$signStr .= $k . '=' . $v . '&';
		}
	}
	$signStr .= 'key=' . $baseCred['app_secret'];
	$params['sign'] = strtoupper(md5($signStr));

	$ch = curl_init();
	curl_setopt_array($ch, [
		CURLOPT_URL => $baseCred['api_url'] . '?action=list',
		CURLOPT_POST => true,
		CURLOPT_POSTFIELDS => http_build_query($params),
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_TIMEOUT => 30,
		CURLOPT_SSL_VERIFYPEER => false
	]);
	$response = curl_exec($ch);
	curl_close($ch);

	$result = json_decode($response, true);
	if (!$result || $result['code'] != 0) {
		exit('{"code":-1,"msg":"' . ($result['msg'] ?? '获取节点列表失败') . '"}');
	}

	$nodes = $result['data']['nodes'] ?? [];
	$expireTime = $result['data']['expire_time'] ?? null;
	$authType = $result['data']['auth_type'] ?? 1;
	$count = 0;

	$nodeLinkPullSyncMeta = function (array $link) use ($DB) {
		$params = [
			'app_id' => $link['app_id'],
			'node_id' => (int)$link['remote_node_id'],
			'timestamp' => time(),
		];
		ksort($params);
		$signStr = '';
		foreach ($params as $k => $v) {
			if ($k !== 'sign' && $v !== '') {
				$signStr .= $k . '=' . $v . '&';
			}
		}
		$signStr .= 'key=' . $link['app_secret'];
		$params['sign'] = strtoupper(md5($signStr));
		$ch = curl_init();
		curl_setopt_array($ch, [
			CURLOPT_URL => $link['api_url'] . '?action=sync',
			CURLOPT_POST => true,
			CURLOPT_POSTFIELDS => http_build_query($params),
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_TIMEOUT => 15,
			CURLOPT_SSL_VERIFYPEER => false,
		]);
		$response = curl_exec($ch);
		curl_close($ch);
		$result = json_decode($response, true);
		if (!$result || $result['code'] != 0) {
			return false;
		}
		$data = $result['data'];
		$params2 = [
			'app_id' => $link['app_id'],
			'timestamp' => time(),
		];
		ksort($params2);
		$signStr2 = '';
		foreach ($params2 as $k => $v) {
			if ($k !== 'sign' && $v !== '') {
				$signStr2 .= $k . '=' . $v . '&';
			}
		}
		$signStr2 .= 'key=' . $link['app_secret'];
		$params2['sign'] = strtoupper(md5($signStr2));
		$ch2 = curl_init();
		curl_setopt_array($ch2, [
			CURLOPT_URL => $link['api_url'] . '?action=list',
			CURLOPT_POST => true,
			CURLOPT_POSTFIELDS => http_build_query($params2),
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_TIMEOUT => 10,
			CURLOPT_SSL_VERIFYPEER => false,
		]);
		$response2 = curl_exec($ch2);
		curl_close($ch2);
		$result2 = json_decode($response2, true);
		$exp = ($result2 && $result2['code'] == 0) ? ($result2['data']['expire_time'] ?? null) : null;
		$DB->exec(
			"UPDATE pre_node_link SET sync_type = ?, sync_cid = ?, sync_protocol = ?, expire_time = COALESCE(?, expire_time), last_sync_time = NOW() WHERE id = ?",
			[$data['type'] ?? null, $data['cid'] ?? null, $data['protocol'] ?? null, $exp, $link['id']]
		);
		return true;
	};

	$classMap = [];

	foreach ($nodes as $node) {
		$remoteBiz = isset($node['biz']) ? trim((string)$node['biz']) : 'cloud';
		if (!in_array($remoteBiz, ['cloud', 'dg'], true)) {
			$remoteBiz = 'cloud';
		}
		if ($importFilter !== null && $remoteBiz !== $importFilter) {
			continue;
		}
		$pullBiz = $remoteBiz;
		$cred = array_merge($baseCred, ['biz' => $pullBiz]);
		$classBiz = $pullBiz;
		$classMapKey = $pullBiz . ':' . (int)($node['class_id'] ?? 0);

		$nodeLocalClassId = 0;

		if ($local_class_id > 0) {
			$nodeLocalClassId = $local_class_id;
		} elseif ($authType == 3) {
			$nodeLocalClassId = 0;
		} else {
			if (!empty($node['class_name'])) {
				$remoteClassName = $node['class_name'];
				$remoteClassId = (int)($node['class_id'] ?? 0);

				if (isset($classMap[$classMapKey])) {
					$nodeLocalClassId = $classMap[$classMapKey];
				} else {
					$existClass = $DB->getRow("SELECT id FROM pre_login_class WHERE name = ? AND biz = ? LIMIT 1", [$remoteClassName, $classBiz]);
					if ($existClass) {
						$nodeLocalClassId = $existClass['id'];
					} else {
						$maxSort = $DB->getColumn("SELECT sort FROM pre_login_class WHERE biz = ? ORDER BY sort DESC LIMIT 1", [$classBiz]);
						$nextSort = (int)$maxSort + 1;
						$DB->exec("INSERT INTO pre_login_class (`name`, `sort`, `biz`) VALUES (?, ?, ?)", [$remoteClassName, $nextSort, $classBiz]);
						$nodeLocalClassId = $DB->lastInsertId();
					}
					$classMap[$classMapKey] = $nodeLocalClassId;
				}
			}
		}

		$exists = $DB->getColumn("SELECT count(*) FROM pre_node_link WHERE api_url = ? AND remote_node_id = ? AND biz = ?", [$cred['api_url'], $node['node_id'], $cred['biz']]);
		if ($exists > 0) {
			$DB->exec("UPDATE pre_node_link SET expire_time = ?, sync_online_census = ?, local_class_id = ? WHERE api_url = ? AND remote_node_id = ? AND biz = ?",
				[$expireTime, $node['online_census'], $nodeLocalClassId, $cred['api_url'], $node['node_id'], $cred['biz']]);
		} else {
			$DB->exec("INSERT INTO pre_node_link (app_id, app_secret, api_url, remote_node_id, remote_class_id, remote_class_name, local_class_id, custom_name, biz, msg, max, show_online_census, sync_online_census, expire_time, status, addtime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW())",
				[$cred['app_id'], $cred['app_secret'], $cred['api_url'], $node['node_id'], $node['class_id'], $node['class_name'], $nodeLocalClassId, $node['name'], $cred['biz'], $node['msg'], $node['max'], $node['show_online_census'], $node['online_census'], $expireTime]);
			$count++;
		}
		$linkRow = $DB->getRow("SELECT * FROM pre_node_link WHERE api_url = ? AND remote_node_id = ? AND biz = ?", [$cred['api_url'], $node['node_id'], $cred['biz']]);
		if ($linkRow) {
			$nodeLinkPullSyncMeta($linkRow);
		}
	}

	exit(json_encode(['code' => 0, 'msg' => '拉取成功', 'data' => ['count' => $count, 'auth_type' => $authType]]));
break;

case 'node_link_get':
	$id = intval($_GET['id']);
	$row = $DB->getRow("SELECT * FROM pre_node_link WHERE id = ?", [$id]);
	if (!$row) {
		exit('{"code":-1,"msg":"节点不存在"}');
	}
	exit(json_encode(['code' => 0, 'msg' => 'success', 'data' => $row]));
break;

case 'node_link_save':
	$id = intval($_POST['id']);
	$custom_name = trim($_POST['custom_name']);
	$local_class_id = intval($_POST['local_class_id']);
	$show_online_census = intval($_POST['show_online_census'] ?? 1);
	$msg = trim($_POST['msg']);
	$msg_type = intval($_POST['msg_type'] ?? 0);
	$max = intval($_POST['max']);
	$apipwd = trim($_POST['apipwd']);
	$status = intval($_POST['status']);

	if ($local_class_id < 0) $local_class_id = 0;
	$node = $DB->getRow("SELECT id,biz FROM pre_node_link WHERE id = ? LIMIT 1", [$id]);
	if (!$node) {
		exit('{"code":-1,"msg":"节点不存在"}');
	}
	// 分类必须属于该节点业务类型（cloud/dg）
	if ($local_class_id > 0) {
		$loginClass = $DB->getRow("SELECT id,biz FROM pre_login_class WHERE id = ? AND biz IN ('cloud','dg') LIMIT 1", [$local_class_id]);
		if (!$loginClass) {
			exit('{"code":-1,"msg":"本地分类ID不存在或不属于业务分类"}');
		}
		if (($loginClass['biz'] ?? '') !== ($node['biz'] ?? 'cloud')) {
			exit('{"code":-1,"msg":"本地分类ID必须属于当前节点业务类型（' . ($node['biz'] ?? 'cloud') . '）"}');
		}
	}

	// 检查 api_error_check 字段是否存在
	$api_error_check = intval($_POST['api_error_check'] ?? 1);
	$columnExists = $DB->getColumn("SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'pre_node_link' AND column_name = 'api_error_check'");

	if ($columnExists) {
		$sql = "UPDATE pre_node_link SET custom_name = ?, local_class_id = ?, show_online_census = ?, api_error_check = ?, msg = ?, msg_type = ?, max = ?, apipwd = ?, status = ? WHERE id = ?";
		$params = [$custom_name, $local_class_id, $show_online_census, $api_error_check, $msg, $msg_type, $max, $apipwd, $status, $id];
	} else {
		$sql = "UPDATE pre_node_link SET custom_name = ?, local_class_id = ?, show_online_census = ?, msg = ?, msg_type = ?, max = ?, apipwd = ?, status = ? WHERE id = ?";
		$params = [$custom_name, $local_class_id, $show_online_census, $msg, $msg_type, $max, $apipwd, $status, $id];
	}

	if ($DB->exec($sql, $params)) {
		exit('{"code":0,"msg":"保存成功"}');
	} else {
		exit('{"code":-1,"msg":"保存失败"}');
	}
break;

// 批量设置节点本地分类ID（只支持业务类型分类：cloud/dg）
case 'node_link_batch_set_local_class':
	$local_class_id = intval($_POST['local_class_id'] ?? 0);
	if ($local_class_id < 0) $local_class_id = 0;

	$idsRaw = $_POST['ids'] ?? '';
	$ids = [];
	if (is_array($idsRaw)) {
		$ids = $idsRaw;
	} else {
		$idsStr = trim((string)$idsRaw);
		if ($idsStr !== '') {
			$ids = array_filter(array_map('trim', explode(',', $idsStr)));
		}
	}
	$ids = array_values(array_filter(array_map('intval', $ids), function($x){ return $x > 0; }));

	if (count($ids) === 0) {
		exit('{"code":-1,"msg":"未选择任何节点"}');
	}

	$ph = implode(',', array_fill(0, count($ids), '?'));
	$nodes = $DB->query("SELECT id,biz FROM pre_node_link WHERE id IN ($ph)", $ids);
	$nodeRows = [];
	while ($row = $nodes->fetch(PDO::FETCH_ASSOC)) {
		$nodeRows[] = $row;
	}
	if (count($nodeRows) !== count($ids)) {
		exit('{"code":-1,"msg":"部分节点不存在"}');
	}

	if ($local_class_id > 0) {
		$loginClass = $DB->getRow("SELECT id,biz FROM pre_login_class WHERE id=? LIMIT 1", [$local_class_id]);
		if (!$loginClass) {
			exit('{"code":-1,"msg":"本地分类ID不存在"}');
		}
		$classBiz = $loginClass['biz'] ?? 'cloud';
		foreach ($nodeRows as $n) {
			if (($n['biz'] ?? 'cloud') !== $classBiz) {
				exit('{"code":-1,"msg":"批量设置失败：分类必须是所选节点对应业务类型（' . $classBiz . '）的分类ID"}');
			}
		}
	}

	$params = array_merge([$local_class_id], $ids);
	$DB->exec("UPDATE pre_node_link SET local_class_id = ? WHERE id IN ($ph)", $params);
	exit('{"code":0,"msg":"批量设置成功"}');
break;

case 'node_link_sync':
	$id = intval($_POST['id']);
	
	$node = $DB->getRow("SELECT * FROM pre_node_link WHERE id = ?", [$id]);
	if (!$node) {
		exit('{"code":-1,"msg":"节点不存在"}');
	}
	
	// 调用被对接方API同步框架类型和协议
	$params = [
		'app_id' => $node['app_id'],
		'node_id' => $node['remote_node_id'],
		'timestamp' => time()
	];
	$signStr = 'app_id=' . $node['app_id'] . '&node_id=' . $params['node_id'] . '&timestamp=' . $params['timestamp'] . '&key=' . $node['app_secret'];
	$params['sign'] = strtoupper(md5($signStr));
	
	$ch = curl_init();
	curl_setopt_array($ch, [
		CURLOPT_URL => $node['api_url'] . '?action=sync',
		CURLOPT_POST => true,
		CURLOPT_POSTFIELDS => http_build_query($params),
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_TIMEOUT => 10,
		CURLOPT_SSL_VERIFYPEER => false
	]);
	$response = curl_exec($ch);
	curl_close($ch);
	
	$result = json_decode($response, true);
	if (!$result || $result['code'] != 0) {
		exit('{"code":-1,"msg":"' . ($result['msg'] ?? '同步失败') . '"}');
	}
	
	$data = $result['data'];
	
	// 同时获取授权信息（包括expire_time）
	$params2 = [
		'app_id' => $node['app_id'],
		'timestamp' => time()
	];
	$signStr2 = 'app_id=' . $node['app_id'] . '&timestamp=' . $params2['timestamp'] . '&key=' . $node['app_secret'];
	$params2['sign'] = strtoupper(md5($signStr2));
	
	$ch2 = curl_init();
	curl_setopt_array($ch2, [
		CURLOPT_URL => $node['api_url'] . '?action=list',
		CURLOPT_POST => true,
		CURLOPT_POSTFIELDS => http_build_query($params2),
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_TIMEOUT => 10,
		CURLOPT_SSL_VERIFYPEER => false
	]);
	$response2 = curl_exec($ch2);
	curl_close($ch2);
	
	$result2 = json_decode($response2, true);
	$expireTime = $result2['data']['expire_time'] ?? null;
	
	$DB->exec("UPDATE pre_node_link SET sync_type = ?, sync_cid = ?, sync_protocol = ?, expire_time = ?, last_sync_time = NOW() WHERE id = ?",
		[$data['type'], $data['cid'], $data['protocol'], $expireTime, $id]);
	
	exit('{"code":0,"msg":"同步成功"}');
break;

case 'node_link_sync_all':
	// 同步所有对接节点
	$rs = $DB->query("SELECT * FROM pre_node_link WHERE status = 1");
	$count = 0;
	
	while ($node = $rs->fetch(PDO::FETCH_ASSOC)) {
		$params = [
			'app_id' => $node['app_id'],
			'node_id' => $node['remote_node_id'],
			'timestamp' => time()
		];
		$signStr = 'app_id=' . $node['app_id'] . '&node_id=' . $params['node_id'] . '&timestamp=' . $params['timestamp'] . '&key=' . $node['app_secret'];
		$params['sign'] = strtoupper(md5($signStr));
		
		$ch = curl_init();
		curl_setopt_array($ch, [
			CURLOPT_URL => $node['api_url'] . '?action=sync',
			CURLOPT_POST => true,
			CURLOPT_POSTFIELDS => http_build_query($params),
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_TIMEOUT => 10,
			CURLOPT_SSL_VERIFYPEER => false
		]);
		$response = curl_exec($ch);
		curl_close($ch);
		
		$result = json_decode($response, true);
		if ($result && $result['code'] == 0) {
			$data = $result['data'];
			$DB->exec("UPDATE pre_node_link SET sync_type = ?, sync_cid = ?, sync_protocol = ?, last_sync_time = NOW() WHERE id = ?",
				[$data['type'], $data['cid'], $data['protocol'], $node['id']]);
			$count++;
		}
	}
	
	exit(json_encode(['code' => 0, 'msg' => '同步完成', 'data' => ['count' => $count]]));
break;

case 'node_link_sync_expire':
	// 手动同步授权时长
	$app_id = trim($_POST['app_id'] ?? '');
	$cred_biz = isset($_POST['biz']) ? trim($_POST['biz']) : '';
	$cred_api_url = isset($_POST['api_url']) ? trim($_POST['api_url']) : '';
	
	if ($app_id) {
		if ($cred_api_url !== '' && in_array($cred_biz, ['cloud', 'dg'], true)) {
			$nodes = $DB->query("SELECT * FROM pre_node_link WHERE app_id = ? AND api_url = ? AND biz = ?", [$app_id, $cred_api_url, $cred_biz]);
		} elseif (in_array($cred_biz, ['cloud', 'dg'], true)) {
			$nodes = $DB->query("SELECT * FROM pre_node_link WHERE app_id = ? AND biz = ?", [$app_id, $cred_biz]);
		} else {
			$nodes = $DB->query("SELECT * FROM pre_node_link WHERE app_id = ?", [$app_id]);
		}
	} else {
		// 同步所有节点
		$nodes = $DB->query("SELECT * FROM pre_node_link WHERE status = 1");
	}
	
	$count = 0;
	while ($node = $nodes->fetch(PDO::FETCH_ASSOC)) {
		// 调用被对接方API获取最新授权信息
		$params = [
			'app_id' => $node['app_id'],
			'timestamp' => time()
		];
		$signStr = 'app_id=' . $node['app_id'] . '&timestamp=' . $params['timestamp'] . '&key=' . $node['app_secret'];
		$params['sign'] = strtoupper(md5($signStr));
		
		$ch = curl_init();
		curl_setopt_array($ch, [
			CURLOPT_URL => $node['api_url'] . '?action=list',
			CURLOPT_POST => true,
			CURLOPT_POSTFIELDS => http_build_query($params),
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_TIMEOUT => 10,
			CURLOPT_SSL_VERIFYPEER => false
		]);
		$response = curl_exec($ch);
		curl_close($ch);
		
		$result = json_decode($response, true);
		if ($result && $result['code'] == 0) {
			$expireTime = $result['data']['expire_time'] ?? null;
			$DB->exec("UPDATE pre_node_link SET expire_time = ? WHERE id = ?", [$expireTime, $node['id']]);
			$count++;
		}
	}
	
	exit(json_encode(['code' => 0, 'msg' => '同步成功', 'data' => ['count' => $count]]));
break;

case 'node_link_sort':
	$id = intval($_POST['id']);
	$sort = intval($_POST['sort']);
	if ($DB->exec("UPDATE pre_node_link SET sort = ? WHERE id = ?", [$sort, $id])) {
		exit('{"code":0,"msg":"排序成功"}');
	} else {
		exit('{"code":-1,"msg":"排序失败"}');
	}
break;

case 'node_link_delete':
	$id = intval($_POST['id']);
	if ($DB->exec("DELETE FROM pre_node_link WHERE id = ?", [$id])) {
		exit('{"code":0,"msg":"删除成功"}');
	} else {
		exit('{"code":-1,"msg":"删除失败"}');
	}
break;

case 'node_link_delete_credential':
	$app_id = trim($_POST['app_id']);
	$cred_biz = isset($_POST['biz']) ? trim($_POST['biz']) : '';
	$cred_api_url = isset($_POST['api_url']) ? trim($_POST['api_url']) : '';
	if ($cred_api_url !== '' && in_array($cred_biz, ['cloud', 'dg'], true)) {
		$DB->exec("DELETE FROM pre_node_link WHERE app_id = ? AND api_url = ? AND biz = ?", [$app_id, $cred_api_url, $cred_biz]);
	} elseif (in_array($cred_biz, ['cloud', 'dg'], true)) {
		$DB->exec("DELETE FROM pre_node_link WHERE app_id = ? AND biz = ?", [$app_id, $cred_biz]);
	} else {
		$DB->exec("DELETE FROM pre_node_link WHERE app_id = ?", [$app_id]);
	}
	exit('{"code":0,"msg":"删除成功"}');
break;

// ========== 小荠菜优化功能 ==========
case 'save_optimize_stats':
	$stats_cloud_add = isset($_POST['stats_cloud_add']) ? trim($_POST['stats_cloud_add']) : '';
	$stats_dg_add = isset($_POST['stats_dg_add']) ? trim($_POST['stats_dg_add']) : '';
	$stats_qzone_add = isset($_POST['stats_qzone_add']) ? trim($_POST['stats_qzone_add']) : '';
	$bugua_version = isset($_POST['bugua_version']) ? trim($_POST['bugua_version']) : '';
	
	// 验证输入（只能是数字或空）
	$validate = function($val) {
		return $val === '' || is_numeric($val);
	};
	
	if (!$validate($stats_cloud_add) || !$validate($stats_dg_add) || !$validate($stats_qzone_add)) {
		exit(json_encode(['code' => -1, 'msg' => '请输入有效的数字']));
	}
	
	// 保存配置
	$stats_cloud_add = $stats_cloud_add !== '' ? intval($stats_cloud_add) : '';
	$stats_dg_add = $stats_dg_add !== '' ? intval($stats_dg_add) : '';
	$stats_qzone_add = $stats_qzone_add !== '' ? intval($stats_qzone_add) : '';
	
	$DB->query("REPLACE INTO pre_config SET v='{$stats_cloud_add}',k='stats_cloud_add'");
	$DB->query("REPLACE INTO pre_config SET v='{$stats_dg_add}',k='stats_dg_add'");
	$DB->query("REPLACE INTO pre_config SET v='{$stats_qzone_add}',k='stats_qzone_add'");
	
	// 保存补挂版本配置
	if($bugua_version !== '' && in_array($bugua_version, ['qb', 'miaomiao'])) {
		$bugua_version = daddslashes($bugua_version);
		$DB->query("REPLACE INTO pre_config SET v='{$bugua_version}',k='bugua_version'");
	}
	
	exit(json_encode(['code' => 1, 'msg' => '保存成功']));
break;

// 检测更新（对接授权站API）
case 'check_update_api':
	$current_version = isset($_POST['current_version']) ? trim($_POST['current_version']) : '1.0';
	
	$api_url = 'https://sqz.dadxb.cn/api/check-update.php?current_version=' . urlencode($current_version);
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $api_url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_TIMEOUT, 10);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	$response = curl_exec($ch);
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);
	
	if($http_code == 200 && $response) {
		$result = json_decode($response, true);
		if($result && isset($result['success']) && $result['success']) {
			exit(json_encode([
				'code' => 1,
				'msg' => '获取成功',
				'data' => [
					'has_update' => $result['data']['has_update'] ?? false,
					'latest_version' => $result['data']['latest_version'] ?? $current_version,
					'name' => $result['data']['name'] ?? '',
					'changelog' => $result['data']['changelog'] ?? '',
					'download_url' => $result['data']['download_url'] ?? ''
				]
			]));
		} else {
			exit(json_encode(['code' => 0, 'msg' => $result['message'] ?? '检测失败']));
		}
	} else {
		exit(json_encode(['code' => -1, 'msg' => '无法连接授权服务器']));
	}
break;

// 检测授权状态（对接授权站API）
case 'check_auth_status_api':
	$domain = isset($_POST['domain']) ? trim($_POST['domain']) : '';
	
	if(empty($domain)) {
		$domain = $_SERVER['HTTP_HOST'] ?? '';
		$domain = preg_replace('/:\d+$/', '', $domain);
	}
	
	if(empty($domain)) {
		exit(json_encode(['code' => -1, 'msg' => '无法获取域名']));
	}
	
	$api_url = 'https://sqz.dadxb.cn/api/verify.php?domain=' . urlencode($domain);
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $api_url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_TIMEOUT, 10);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	$response = curl_exec($ch);
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);
	
	if($http_code == 200 && $response) {
		$result = json_decode($response, true);
		if($result && isset($result['success']) && $result['success'] && isset($result['data']['is_valid']) && $result['data']['is_valid']) {
			exit(json_encode([
				'code' => 1,
				'msg' => '授权有效',
				'data' => [
					'is_valid' => true,
					'domain' => $result['data']['domain'] ?? $domain,
					'expire_time' => $result['data']['expire_time'] ?? '',
					'days_left' => $result['data']['days_left'] ?? 0,
					'card_key' => $result['data']['card_key'] ?? ''
				]
			]));
		} else {
			exit(json_encode([
				'code' => 0,
				'msg' => $result['message'] ?? '未授权或授权已过期',
				'data' => ['is_valid' => false]
			]));
		}
	} else {
		exit(json_encode(['code' => -1, 'msg' => '无法连接授权服务器', 'data' => ['is_valid' => false]]));
	}
break;

case 'check_schema':
	// 定义核心必需字段（只检查这些关键字段是否存在）
	// 注意：只检查核心字段，可选字段不检查，避免定制化数据库误报
	$core_required = [
		'qq' => ['id', 'qq', 'pwd', 'zid', 'endtime_arr'],  // 核心QQ表
		'dg_qq' => ['id', 'qq', 'pwd', 'zid', 'endtime_arr'],  // 代挂QQ表
		'qzone_qq' => ['id', 'qq'],  // Qzone表
		'loginconfig' => ['id', 'url', 'status'],  // 登录配置
		'protocol' => ['id'],  // 协议表
		'config' => ['k', 'v'],  // 配置表（核心）
		'user' => ['zid', 'user', 'pwd'],  // 用户表（核心）
		'pay' => ['trade_no', 'money', 'status', 'zid'],  // 支付表
		'goods' => ['id', 'name', 'price', 'status'],  // 商品表
		'km' => ['id', 'km'],  // 卡密表
		'login_class' => ['id', 'name'],  // 登录分类
		'message' => ['id', 'title']  // 消息表
	];
	
	$missing_tables = [];
	$extra_tables = [];
	$tables_result = [];
	
	// 获取数据库中所有表，并自动检测表前缀
	$all_tables = [];
	$table_prefix = '';
	$rs = $DB->query("SHOW TABLES");
	if($rs) {
		while($row = $rs->fetch(PDO::FETCH_NUM)) {
			$table_name = $row[0];
			$all_tables[] = $table_name;
		}
	}
	
	// 自动检测表前缀：优先匹配 config 表（最常见），其次是 qq 表
	foreach($all_tables as $table_name) {
		if(preg_match('/^(.+)_config$/', $table_name, $matches)) {
			$table_prefix = $matches[1] . '_';
			break;
		}
	}
	if(empty($table_prefix)) {
		foreach($all_tables as $table_name) {
			if(preg_match('/^(.+)_qq$/', $table_name, $matches) && !preg_match('/_dg_qq$/', $table_name)) {
				$table_prefix = $matches[1] . '_';
				break;
			}
		}
	}
	if(empty($table_prefix)) {
		foreach($all_tables as $table_name) {
			if(preg_match('/^(.+)_dg_qq$/', $table_name, $matches)) {
				$table_prefix = $matches[1] . '_';
				break;
			}
		}
	}
	
	if(empty($table_prefix)) {
		global $dbconfig;
		$table_prefix = isset($dbconfig['pre']) ? $dbconfig['pre'] : 'pre_';
	}
	
	// 获取匹配前缀的表（不含前缀）
	$existing_tables = [];
	foreach($all_tables as $table_name) {
		if(strpos($table_name, $table_prefix) === 0) {
			$existing_tables[] = substr($table_name, strlen($table_prefix));
		}
	}
	
	// 检查每个表的核心字段
	foreach($core_required as $table => $required_columns) {
		if(!in_array($table, $existing_tables)) {
			$missing_tables[] = $table_prefix . $table;
			continue;
		}
		
		$full_table_name = $table_prefix . $table;
		
		$existing_columns = [];
		$rs = $DB->query("SHOW COLUMNS FROM `{$full_table_name}`");
		if($rs) {
			while($row = $rs->fetch(PDO::FETCH_ASSOC)) {
				$existing_columns[] = $row['Field'];
			}
		}
		
		// 只检查核心字段是否缺失
		$missing_columns = array_diff($required_columns, $existing_columns);
		
		// 只记录真正缺失核心字段的表
		if(count($missing_columns) > 0) {
			$tables_result[] = [
				'table' => $table_prefix . $table,
				'missing_columns' => array_values($missing_columns),
				'extra_columns' => []  // 不再显示扩展列，避免混淆
			];
		}
	}
	
	// 计算总数
	$total_missing_cols = 0;
	foreach($tables_result as $t) {
		$total_missing_cols += count($t['missing_columns']);
	}
	
	exit(json_encode([
		'code' => 1,
		'msg' => '校验完成',
		'data' => [
			'missing_tables' => $missing_tables,
			'extra_tables' => [],
			'tables' => $tables_result,
			'summary' => [
				'missing_tables_count' => count($missing_tables),
				'missing_columns_count' => $total_missing_cols,
				'extra_columns_count' => 0  // 不再统计扩展列
			]
		]
	]));
break;

default:
	exit('{"code":-4,"msg":"No Act"}');
break;
}
