'localhost', '端口' => 3306, '库名' => 'asipk', '用户' => 'root', '密码' => '', '表名' => 'asipk', ]; // 微信公众号配置 $微信配置 = [ 'appid' => '', // 填写你的AppID 'appsecret' => '', // 填写你的AppSecret 'token' => 'asipk_verify_token_2024', // 自定义Token ]; // 安全与系统配置 $系统配置 = [ '加密密钥' => 'asipk_aes_secret_key_2024!', '会话超时' => 86400, '上传限制' => 10485760, '频率限制' => 10, '调试模式' => true, // 生产环境请设为false '默认项目' => ['asihtml','asi-css','asi-js','asictx','asiurl'], '子目录' => ['html','css','js','md','json','png','jpg','mp3','mp4'], '允许扩展名' => ['html','css','js','md','json','png','jpg','mp3','mp4','txt'], ]; // AI模型API地址配置 $AI地址表 = [ 'deepseek' => 'https://api.deepseek.com/v1/chat/completions', 'kimi' => 'https://api.moonshot.cn/v1/chat/completions', 'zhipu' => 'https://open.bigmodel.cn/api/paas/v4/chat/completions', 'poe' => 'https://api.poe.com/v1/chat/completions', 'doubao' => 'https://ark.cn-beijing.volces.com/api/v3/chat/completions', 'gemini' => 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions', 'chatgpt' => 'https://api.openai.com/v1/chat/completions', ]; // AI模型默认名称映射 $AI模型表 = [ 'deepseek' => ['名称'=>'DeepSeek','模型'=>'deepseek-chat'], 'kimi' => ['名称'=>'Kimi','模型'=>'moonshot-v1-8k'], 'zhipu' => ['名称'=>'智谱GLM','模型'=>'glm-4-flash'], 'poe' => ['名称'=>'Poe','模型'=>'gpt-3.5-turbo'], 'doubao' => ['名称'=>'豆包','模型'=>'doubao-pro-4k'], 'gemini' => ['名称'=>'Gemini','模型'=>'gemini-pro'], 'chatgpt' => ['名称'=>'ChatGPT','模型'=>'gpt-4o-mini'], ]; /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第二部分:核心初始化 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED); ini_set('display_errors', 0); date_default_timezone_set('Asia/Shanghai'); if (session_status() === PHP_SESSION_NONE) session_start(); $根目录 = rtrim(__DIR__, '/\\'); $数据库 = null; /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第三部分:工具函数 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** JSON响应输出 */ function 响应($数据, $码 = 200) { http_response_code($码); header('Content-Type: application/json; charset=utf-8'); echo json_encode($数据, JSON_UNESCAPED_UNICODE); exit; } /** 获取请求参数 */ function 取参($键, $默认 = '') { return isset($_REQUEST[$键]) ? trim($_REQUEST[$键]) : $默认; } /** 获取POST JSON数据 */ function 取JSON() { $原始 = file_get_contents('php://input'); return $原始 ? json_decode($原始, true) : []; } /** XSS过滤 */ function 净化($文本) { if (is_array($文本)) return array_map('净化', $文本); return htmlspecialchars(strip_tags(trim($文本)), ENT_QUOTES, 'UTF-8'); } /** 简单AES加密 */ function 加密($明文) { global $系统配置; $密钥 = substr(hash('sha256', $系统配置['加密密钥']), 0, 32); $iv = substr(hash('sha256', 'asipk_iv'), 0, 16); return base64_encode(openssl_encrypt($明文, 'AES-256-CBC', $密钥, 0, $iv)); } /** 简单AES解密 */ function 解密($密文) { global $系统配置; $密钥 = substr(hash('sha256', $系统配置['加密密钥']), 0, 32); $iv = substr(hash('sha256', 'asipk_iv'), 0, 16); return openssl_decrypt(base64_decode($密文), 'AES-256-CBC', $密钥, 0, $iv); } /** CSRF Token生成 */ function 生成CSRF() { if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } /** CSRF验证 */ function 验证CSRF() { $token = 取参('_csrf') ?: ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''); if (empty($token) || $token !== ($_SESSION['csrf_token'] ?? '')) { 响应(['错误' => 'CSRF验证失败'], 403); } } /** 验证登录状态 */ function 需要登录() { global $系统配置; if (empty($_SESSION['用户id'])) { 响应(['错误' => '未登录', '需登录' => true], 401); } if (isset($_SESSION['登录时间']) && (time() - $_SESSION['登录时间'] > $系统配置['会话超时'])) { session_destroy(); 响应(['错误' => '登录已过期', '需登录' => true], 401); } } /** 获取当前时间字符串 */ function 现在() { return date('Y-m-d H:i:s'); } /** 安全路径检查(防止目录遍历) */ function 安全路径($路径) { global $根目录; $真实路径 = realpath($路径); if ($真实路径 === false) { $真实路径 = realpath(dirname($路径)); if ($真实路径 === false) return false; $真实路径 .= '/' . basename($路径); } $根真实 = realpath($根目录); return strpos($真实路径, $根真实) === 0 ? $真实路径 : false; } /** 记录操作日志 */ function 记录日志($类型, $内容, $对象 = '', $结果 = '成功') { try { $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $sql = "INSERT INTO `{$表}` (`章`,`节`,`题`,`广`,`串`,`户`,`谁`,`时`,`群`) VALUES (?,?,?,?,?,?,?,?,?)"; $st = $db->prepare($sql); $st->execute([ '操作日志', $类型, $对象, $内容, $结果, $_SESSION['用户id'] ?? '', $_SESSION['用户名'] ?? '', 现在(), '日志' ]); } catch (Exception $e) { // 日志记录失败不影响主流程 } } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第四部分:数据库操作 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** 连接数据库(自动创建库和表) */ function 连接数据库() { global $数据库配置, $数据库; if ($数据库 !== null) return $数据库; try { $dsn = "mysql:host={$数据库配置['主机']};port={$数据库配置['端口']};charset=utf8mb4"; $pdo = new PDO($dsn, $数据库配置['用户'], $数据库配置['密码'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); $pdo->exec("CREATE DATABASE IF NOT EXISTS `{$数据库配置['库名']}` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); $pdo->exec("USE `{$数据库配置['库名']}`"); $数据库 = $pdo; 初始化表(); return $数据库; } catch (PDOException $e) { if (取参('动') !== '') { 响应(['错误' => '数据库连接失败: ' . $e->getMessage()], 500); } return null; } } /** 初始化广表 */ function 初始化表() { global $数据库, $数据库配置; $表 = $数据库配置['表名']; $sql = "CREATE TABLE IF NOT EXISTS `{$表}` ( `号` int(11) NOT NULL AUTO_INCREMENT, `广` mediumtext COMMENT '通用', `文` mediumtext COMMENT '文本', `章` varchar(500) DEFAULT NULL COMMENT '大类', `节` varchar(500) DEFAULT NULL COMMENT '小类', `题` varchar(500) DEFAULT NULL COMMENT '标题', `滤` varchar(500) DEFAULT NULL COMMENT '滤镜', `签` varchar(500) DEFAULT NULL COMMENT '标签', `价` varchar(500) DEFAULT NULL COMMENT '定价', `为` varchar(500) DEFAULT NULL COMMENT '布尔值', `几` varchar(500) DEFAULT NULL COMMENT '数值', `串` varchar(500) DEFAULT NULL COMMENT '字符串', `阵` varchar(500) DEFAULT NULL COMMENT '数组', `物` mediumtext COMMENT '对象', `链` varchar(500) DEFAULT NULL COMMENT '链接', `评` varchar(500) DEFAULT NULL COMMENT '评论', `赞` varchar(500) DEFAULT NULL COMMENT '点赞', `函` varchar(500) DEFAULT NULL COMMENT '函数', `司` varchar(500) DEFAULT NULL COMMENT '主体', `群` varchar(500) DEFAULT NULL COMMENT '群体', `组` varchar(500) DEFAULT NULL COMMENT '小组', `排` varchar(500) DEFAULT NULL COMMENT '排序', `隐` varchar(500) DEFAULT NULL COMMENT '隐藏', `谁` varchar(500) DEFAULT NULL COMMENT '昵称', `户` varchar(500) DEFAULT NULL COMMENT '用户', `时` varchar(500) DEFAULT NULL COMMENT '时间', PRIMARY KEY (`号`), KEY `idx_章` (`章`(250)), KEY `idx_节` (`节`(250)), KEY `idx_组` (`组`(250)), KEY `idx_户` (`户`(250)), KEY `idx_群` (`群`(250)) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"; $数据库->exec($sql); } /** 广表插入 */ function 插入($数据) { $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $字段 = array_keys($数据); $占位 = array_fill(0, count($字段), '?'); $cols = implode(',', array_map(function($f){ return "`{$f}`"; }, $字段)); $vals = implode(',', $占位); $sql = "INSERT INTO `{$表}` ({$cols}) VALUES ({$vals})"; $st = $db->prepare($sql); $st->execute(array_values($数据)); return $db->lastInsertId(); } /** 广表查询 */ function 查询($条件 = [], $排序 = '`号` DESC', $限制 = 100, $偏移 = 0) { $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $where = ''; $参数 = []; if (!empty($条件)) { $片段 = []; foreach ($条件 as $键 => $值) { if (is_array($值)) { $片段[] = "`{$键}` {$值[0]} ?"; $参数[] = $值[1]; } else { $片段[] = "`{$键}` = ?"; $参数[] = $值; } } $where = 'WHERE ' . implode(' AND ', $片段); } $sql = "SELECT * FROM `{$表}` {$where} ORDER BY {$排序} LIMIT {$限制} OFFSET {$偏移}"; $st = $db->prepare($sql); $st->execute($参数); return $st->fetchAll(); } /** 广表更新 */ function 更新($号, $数据) { $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $设置 = []; $参数 = []; foreach ($数据 as $键 => $值) { $设置[] = "`{$键}` = ?"; $参数[] = $值; } $参数[] = $号; $sql = "UPDATE `{$表}` SET " . implode(',', $设置) . " WHERE `号` = ?"; $st = $db->prepare($sql); return $st->execute($参数); } /** 广表删除 */ function 删除($号) { $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $st = $db->prepare("DELETE FROM `{$表}` WHERE `号` = ?"); return $st->execute([$号]); } /** 广表计数 */ function 计数($条件 = []) { $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $where = ''; $参数 = []; if (!empty($条件)) { $片段 = []; foreach ($条件 as $键 => $值) { if (is_array($值)) { $片段[] = "`{$键}` {$值[0]} ?"; $参数[] = $值[1]; } else { $片段[] = "`{$键}` = ?"; $参数[] = $值; } } $where = 'WHERE ' . implode(' AND ', $片段); } $st = $db->prepare("SELECT COUNT(*) as 总数 FROM `{$表}` {$where}"); $st->execute($参数); $行 = $st->fetch(); return (int)$行['总数']; } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第五部分:目录/文件管理 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** 初始化默认项目目录结构 */ function 初始化目录() { global $根目录, $系统配置; foreach ($系统配置['默认项目'] as $项目) { 创建项目目录($项目); } } /** 为项目创建完整目录结构 */ function 创建项目目录($项目名) { global $根目录, $系统配置; $项目名 = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $项目名); if (empty($项目名)) return false; $项目路径 = $根目录 . '/' . $项目名; if (!is_dir($项目路径)) mkdir($项目路径, 0755, true); foreach ($系统配置['子目录'] as $子目录) { $子路径 = $项目路径 . '/' . $子目录; if (!is_dir($子路径)) mkdir($子路径, 0755, true); // 创建默认文件 $默认文件 = $子路径 . '/' . $项目名 . '1.' . $子目录; if (!file_exists($默认文件)) { $内容 = 生成默认内容($子目录, $项目名); file_put_contents($默认文件, $内容); } } return true; } /** 生成各类型默认文件内容 */ function 生成默认内容($扩展名, $项目名) { switch ($扩展名) { case 'html': return "\n\n\n\n{$项目名}\n\n\n

{$项目名}

\n\n"; case 'css': return "/* {$项目名} 样式 */\nbody { margin: 0; padding: 20px; font-family: sans-serif; }"; case 'js': return "// {$项目名} 脚本\nconsole.log('{$项目名} loaded');"; case 'md': return "# {$项目名}\n\n项目说明文档"; case 'json': return json_encode(['项目' => $项目名, '版本' => '1.0'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); default: return ''; } } /** 获取项目列表 */ function 获取项目列表() { global $根目录; $列表 = []; $当前文件 = basename(__FILE__); $扫描 = scandir($根目录); foreach ($扫描 as $项) { if ($项 === '.' || $项 === '..') continue; if ($项 === $当前文件) continue; $全路径 = $根目录 . '/' . $项; if (is_dir($全路径) && !in_array($项, ['.git', 'node_modules', '.idea', '__pycache__'])) { $列表[] = [ '名称' => $项, '路径' => $项, '文件数' => 统计文件数($全路径), '修改时间' => date('Y-m-d H:i:s', filemtime($全路径)), ]; } } return $列表; } /** 递归统计文件数 */ function 统计文件数($目录) { $计数 = 0; $迭代 = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($目录, RecursiveDirectoryIterator::SKIP_DOTS)); foreach ($迭代 as $文件) { if ($文件->isFile()) $计数++; } return $计数; } /** 获取项目文件树 */ function 获取文件树($项目名) { global $根目录; $项目路径 = $根目录 . '/' . $项目名; if (!安全路径($项目路径) || !is_dir($项目路径)) return []; return 扫描目录树($项目路径, $项目名); } function 扫描目录树($路径, $相对 = '') { $结果 = []; $项目列表 = scandir($路径); foreach ($项目列表 as $项) { if ($项 === '.' || $项 === '..') continue; $全路径 = $路径 . '/' . $项; $相对路径 = $相对 ? $相对 . '/' . $项 : $项; if (is_dir($全路径)) { $结果[] = [ '名称' => $项, '路径' => $相对路径, '类型' => 'dir', '子项' => 扫描目录树($全路径, $相对路径), ]; } else { $结果[] = [ '名称' => $项, '路径' => $相对路径, '类型' => 'file', '大小' => filesize($全路径), '扩展名' => pathinfo($项, PATHINFO_EXTENSION), '修改时间' => date('Y-m-d H:i:s', filemtime($全路径)), ]; } } return $结果; } /** 读取文件内容 */ function 读取文件($相对路径) { global $根目录; $全路径 = $根目录 . '/' . $相对路径; $安全 = 安全路径($全路径); if (!$安全 || !is_file($全路径)) return null; return file_get_contents($全路径); } /** 保存文件 */ function 保存文件($相对路径, $内容) { global $根目录; $全路径 = $根目录 . '/' . $相对路径; $目录 = dirname($全路径); if (!is_dir($目录)) mkdir($目录, 0755, true); $检查 = 安全路径($目录); if (!$检查) return false; return file_put_contents($全路径, $内容) !== false; } /** 自动编号保存文件 */ function 自动编号保存($项目名, $扩展名, $内容) { global $根目录; $目标目录 = $根目录 . '/' . $项目名 . '/' . $扩展名; if (!is_dir($目标目录)) mkdir($目标目录, 0755, true); // 扫描现有asipk编号文件,获取最大编号 $最大编号 = 0; $文件列表 = glob($目标目录 . '/asipk*.' . $扩展名); foreach ($文件列表 as $文件) { $文件名 = basename($文件, '.' . $扩展名); if (preg_match('/^asipk(\d+)$/', $文件名, $匹配)) { $编号 = intval($匹配[1]); if ($编号 > $最大编号) $最大编号 = $编号; } } $新编号 = $最大编号 + 1; $新文件名 = "asipk{$新编号}.{$扩展名}"; $新路径 = $目标目录 . '/' . $新文件名; if (file_put_contents($新路径, $内容) !== false) { return [ '文件名' => $新文件名, '路径' => $项目名 . '/' . $扩展名 . '/' . $新文件名, '编号' => $新编号, ]; } return false; } /** 删除文件 */ function 删除文件($相对路径) { global $根目录; $全路径 = $根目录 . '/' . $相对路径; $安全 = 安全路径($全路径); if (!$安全 || !file_exists($全路径)) return false; return is_dir($全路径) ? 递归删除($全路径) : unlink($全路径); } function 递归删除($目录) { $项 = scandir($目录); foreach ($项 as $子) { if ($子 === '.' || $子 === '..') continue; $路径 = $目录 . '/' . $子; is_dir($路径) ? 递归删除($路径) : unlink($路径); } return rmdir($目录); } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第六部分:微信公众号登录 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** 微信服务器验证(GET请求) */ function 微信验证() { global $微信配置; $签名 = $_GET['signature'] ?? ''; $时间戳 = $_GET['timestamp'] ?? ''; $随机串 = $_GET['nonce'] ?? ''; $回显 = $_GET['echostr'] ?? ''; $arr = [$微信配置['token'], $时间戳, $随机串]; sort($arr, SORT_STRING); $计算签名 = sha1(implode('', $arr)); if ($计算签名 === $签名) { echo $回显; } else { echo 'verification failed'; } exit; } /** 微信事件推送处理(POST请求) */ function 微信事件处理() { $原始 = file_get_contents('php://input'); if (empty($原始)) exit('success'); $xml = simplexml_load_string($原始, 'SimpleXMLElement', LIBXML_NOCDATA); if (!$xml) exit('success'); $类型 = strtolower((string)$xml->MsgType); $事件 = strtolower((string)($xml->Event ?? '')); $openid = (string)$xml->FromUserName; $场景 = ''; // 处理扫码事件 if ($类型 === 'event' && ($事件 === 'scan' || $事件 === 'subscribe')) { $场景 = (string)($xml->EventKey ?? ''); if ($事件 === 'subscribe' && strpos($场景, 'qrscene_') === 0) { $场景 = substr($场景, 8); } if (!empty($场景) && strpos($场景, 'login_') === 0) { // 获取用户信息 $用户信息 = 获取微信用户信息($openid); // 存储登录状态到数据库 $登录数据 = 查询(['章' => '扫码登录', '节' => $场景], '`号` DESC', 1); if (!empty($登录数据)) { 更新($登录数据[0]['号'], [ '户' => $openid, '谁' => $用户信息['昵称'] ?? '', '链' => $用户信息['头像'] ?? '', '为' => '已扫码', '时' => 现在(), ]); } // 确保用户记录存在 确保用户存在($openid, $用户信息); } } // 响应空消息 echo 'success'; exit; } /** 获取微信用户基本信息 */ function 获取微信用户信息($openid) { global $微信配置; $token = 获取微信AccessToken(); if (empty($token)) return ['昵称' => '微信用户', '头像' => '']; $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$token}&openid={$openid}&lang=zh_CN"; $响应 = file_get_contents($url); $数据 = json_decode($响应, true); return [ '昵称' => $数据['nickname'] ?? '微信用户', '头像' => $数据['headimgurl'] ?? '', ]; } /** 获取微信Access Token(缓存在数据库) */ function 获取微信AccessToken() { global $微信配置; if (empty($微信配置['appid'])) return ''; // 先从数据库查缓存 $缓存 = 查询(['章' => '系统配置', '节' => 'wx_access_token'], '`号` DESC', 1); if (!empty($缓存)) { $存储时间 = strtotime($缓存[0]['时']); if ((time() - $存储时间) < 7000) { return 解密($缓存[0]['串']); } } // 请求新的token $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$微信配置['appid']}&secret={$微信配置['appsecret']}"; $响应 = file_get_contents($url); $数据 = json_decode($响应, true); if (isset($数据['access_token'])) { $加密token = 加密($数据['access_token']); if (!empty($缓存)) { 更新($缓存[0]['号'], ['串' => $加密token, '时' => 现在()]); } else { 插入(['章' => '系统配置', '节' => 'wx_access_token', '串' => $加密token, '时' => 现在(), '群' => '系统']); } return $数据['access_token']; } return ''; } /** 生成带场景的临时二维码 */ function 生成登录二维码() { $场景值 = 'login_' . bin2hex(random_bytes(16)); // 记录到数据库 插入([ '章' => '扫码登录', '节' => $场景值, '为' => '等待扫码', '时' => 现在(), '群' => '登录', ]); $token = 获取微信AccessToken(); if (empty($token)) { // 调试模式下返回模拟二维码 global $系统配置; if ($系统配置['调试模式']) { return ['场景' => $场景值, '二维码' => '', '调试' => true]; } return ['错误' => '无法获取AccessToken']; } $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$token}"; $数据 = json_encode([ 'expire_seconds' => 300, 'action_name' => 'QR_STR_SCENE', 'action_info' => ['scene' => ['scene_str' => $场景值]], ]); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $数据, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], ]); $响应 = curl_exec($ch); curl_close($ch); $结果 = json_decode($响应, true); if (isset($结果['ticket'])) { $二维码URL = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" . urlencode($结果['ticket']); return ['场景' => $场景值, '二维码' => $二维码URL]; } return ['场景' => $场景值, '二维码' => '', '错误' => '生成二维码失败']; } /** 检查扫码登录状态 */ function 检查登录状态($场景值) { $记录 = 查询(['章' => '扫码登录', '节' => $场景值], '`号` DESC', 1); if (empty($记录)) return ['状态' => '无效']; $行 = $记录[0]; if ($行['为'] === '已扫码' && !empty($行['户'])) { // 登录成功,设置session $_SESSION['用户id'] = $行['户']; $_SESSION['用户名'] = $行['谁']; $_SESSION['头像'] = $行['链']; $_SESSION['登录时间'] = time(); 记录日志('登录', '微信扫码登录成功', $行['户']); return ['状态' => '已登录', '用户名' => $行['谁'], '头像' => $行['链']]; } return ['状态' => $行['为']]; } /** 确保用户信息存储 */ function 确保用户存在($openid, $信息) { $已有 = 查询(['章' => '用户信息', '户' => $openid], '`号` DESC', 1); if (empty($已有)) { 插入([ '章' => '用户信息', '节' => '注册', '户' => $openid, '谁' => $信息['昵称'] ?? '微信用户', '链' => $信息['头像'] ?? '', '时' => 现在(), '群' => '用户', ]); } else { 更新($已有[0]['号'], [ '谁' => $信息['昵称'] ?? $已有[0]['谁'], '链' => $信息['头像'] ?? $已有[0]['链'], ]); } } /** 调试模式快速登录(仅在调试模式下有效) */ function 调试登录() { global $系统配置; if (!$系统配置['调试模式']) 响应(['错误' => '调试模式未开启'], 403); $_SESSION['用户id'] = 'debug_user_001'; $_SESSION['用户名'] = '调试用户'; $_SESSION['头像'] = ''; $_SESSION['登录时间'] = time(); 确保用户存在('debug_user_001', ['昵称' => '调试用户', '头像' => '']); 响应(['成功' => true, '用户名' => '调试用户']); } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第七部分:AI流式代理 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** 获取指定AI模型的API Key(从数据库解密) */ function 获取AI密钥($模型id) { $记录 = 查询([ '章' => 'AI密钥', '节' => $模型id, '户' => $_SESSION['用户id'] ?? '', ], '`号` DESC', 1); if (!empty($记录) && !empty($记录[0]['串'])) { return 解密($记录[0]['串']); } // 也尝试查找全局密钥 $全局 = 查询(['章' => 'AI密钥', '节' => $模型id, '群' => '全局'], '`号` DESC', 1); if (!empty($全局) && !empty($全局[0]['串'])) { return 解密($全局[0]['串']); } return ''; } /** AI流式代理请求 */ function AI流式请求() { 需要登录(); $输入 = 取JSON(); $模型id = $输入['模型'] ?? 'deepseek'; $消息 = $输入['消息'] ?? []; $温度 = floatval($输入['温度'] ?? 0.7); $最大长度 = intval($输入['最大长度'] ?? 4096); $模型名 = $输入['模型名'] ?? ''; global $AI地址表, $AI模型表; // 获取API地址 $API地址 = ''; if (isset($AI地址表[$模型id])) { $API地址 = $AI地址表[$模型id]; } else { // 查询自定义模型 $自定义 = 查询(['章' => 'AI自定义模型', '节' => $模型id], '`号` DESC', 1); if (!empty($自定义)) { $配置 = json_decode($自定义[0]['物'], true); $API地址 = $配置['地址'] ?? ''; } } if (empty($API地址)) { header('Content-Type: text/event-stream'); echo "data: " . json_encode(['error' => '模型未配置API地址']) . "\n\n"; echo "data: [DONE]\n\n"; exit; } // 获取API Key $密钥 = 获取AI密钥($模型id); if (empty($密钥)) { header('Content-Type: text/event-stream'); echo "data: " . json_encode(['error' => '请先配置' . ($AI模型表[$模型id]['名称'] ?? $模型id) . '的API Key']) . "\n\n"; echo "data: [DONE]\n\n"; exit; } // 确定模型名称 if (empty($模型名)) { $模型名 = $AI模型表[$模型id]['模型'] ?? $模型id; } // 构建请求体 $请求体 = json_encode([ 'model' => $模型名, 'messages' => $消息, 'stream' => true, 'temperature' => $温度, 'max_tokens' => $最大长度, ], JSON_UNESCAPED_UNICODE); // 设置流式响应头 header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); header('X-Accel-Buffering: no'); // 关闭输出缓冲 while (ob_get_level()) ob_end_clean(); ob_implicit_flush(true); // 记录请求开始 $开始时间 = microtime(true); $总token = 0; $总内容 = ''; // 构建请求头 $请求头 = [ 'Content-Type: application/json', 'Accept: text/event-stream', ]; // 根据模型设置不同的认证方式 if ($模型id === 'gemini') { // Gemini使用URL参数 if (strpos($API地址, '?') === false) { $API地址 .= '?key=' . $密钥; } } else { $请求头[] = 'Authorization: Bearer ' . $密钥; } $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $API地址, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $请求体, CURLOPT_HTTPHEADER => $请求头, CURLOPT_RETURNTRANSFER => false, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 120, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_WRITEFUNCTION => function($ch, $data) use (&$总内容) { // 直接转发流式数据到客户端 $行列表 = explode("\n", $data); foreach ($行列表 as $行) { $行 = trim($行); if (empty($行)) continue; if (strpos($行, 'data: ') === 0) { $json串 = substr($行, 6); if ($json串 === '[DONE]') { echo "data: [DONE]\n\n"; } else { $解析 = json_decode($json串, true); if ($解析 && isset($解析['choices'][0])) { $增量 = $解析['choices'][0]['delta']['content'] ?? ''; if ($增量 !== '') { $总内容 .= $增量; echo "data: " . json_encode(['content' => $增量], JSON_UNESCAPED_UNICODE) . "\n\n"; } } } } else { echo $行 . "\n"; } } if (ob_get_level()) ob_flush(); flush(); return strlen($data); }, ]); $结果 = curl_exec($ch); $错误 = curl_error($ch); $状态码 = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // 确保发送结束标记 echo "data: [DONE]\n\n"; if (ob_get_level()) ob_flush(); flush(); // 记录到数据库 $耗时 = round(microtime(true) - $开始时间, 2); $字数 = mb_strlen($总内容); try { 插入([ '章' => 'AI请求记录', '节' => $模型id, '题' => mb_substr($消息[count($消息)-1]['content'] ?? '', 0, 200), '几' => $字数, '价' => $耗时, '为' => $错误 ? '失败' : '成功', '串' => $状态码, '户' => $_SESSION['用户id'] ?? '', '时' => 现在(), '群' => 'AI', ]); } catch (Exception $e) {} exit; } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第八部分:用户设置与统计 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** 获取用户设置 */ function 获取设置() { 需要登录(); $记录 = 查询(['章' => '用户设置', '户' => $_SESSION['用户id']], '`号` DESC', 1); $设置 = []; if (!empty($记录) && !empty($记录[0]['物'])) { $设置 = json_decode($记录[0]['物'], true) ?: []; } // 合并默认设置 $默认 = [ '主题' => 'dark', '字体大小' => 14, '自动保存' => true, '回车发送' => true, '携带上下文' => true, '上下文长度' => 20, '默认模型' => 'deepseek', '使用本地存储' => true, ]; return array_merge($默认, $设置); } /** 保存用户设置 */ function 保存设置($新设置) { 需要登录(); $记录 = 查询(['章' => '用户设置', '户' => $_SESSION['用户id']], '`号` DESC', 1); $json = json_encode($新设置, JSON_UNESCAPED_UNICODE); if (!empty($记录)) { 更新($记录[0]['号'], ['物' => $json, '时' => 现在()]); } else { 插入([ '章' => '用户设置', '户' => $_SESSION['用户id'], '物' => $json, '时' => 现在(), '群' => '设置', ]); } return true; } /** 保存AI密钥 */ function 保存AI密钥($模型id, $密钥) { 需要登录(); $加密 = 加密($密钥); $记录 = 查询(['章' => 'AI密钥', '节' => $模型id, '户' => $_SESSION['用户id']], '`号` DESC', 1); if (!empty($记录)) { 更新($记录[0]['号'], ['串' => $加密, '时' => 现在()]); } else { 插入([ '章' => 'AI密钥', '节' => $模型id, '串' => $加密, '户' => $_SESSION['用户id'], '时' => 现在(), '群' => '密钥', ]); } return true; } /** 获取统计数据 */ function 获取统计数据() { 需要登录(); $用户 = $_SESSION['用户id']; $db = 连接数据库(); global $数据库配置; $表 = $数据库配置['表名']; $统计 = []; // AI请求统计 $st = $db->prepare("SELECT `节` as 模型, COUNT(*) as 次数, SUM(CAST(`几` AS UNSIGNED)) as 总字数, AVG(CAST(`价` AS DECIMAL(10,2))) as 平均耗时 FROM `{$表}` WHERE `章`='AI请求记录' AND `户`=? GROUP BY `节`"); $st->execute([$用户]); $统计['AI模型'] = $st->fetchAll(); // 今日统计 $今天 = date('Y-m-d'); $st = $db->prepare("SELECT COUNT(*) as 次数 FROM `{$表}` WHERE `章`='AI请求记录' AND `户`=? AND `时` LIKE ?"); $st->execute([$用户, $今天 . '%']); $行 = $st->fetch(); $统计['今日AI请求'] = $行['次数']; // 操作统计 $st = $db->prepare("SELECT `节` as 类型, COUNT(*) as 次数 FROM `{$表}` WHERE `章`='操作日志' AND `户`=? GROUP BY `节`"); $st->execute([$用户]); $统计['操作类型'] = $st->fetchAll(); // 对话统计 $st = $db->prepare("SELECT COUNT(*) as 总数 FROM `{$表}` WHERE `章`='对话' AND `户`=?"); $st->execute([$用户]); $行 = $st->fetch(); $统计['对话总数'] = $行['总数']; // 7天趋势 $趋势 = []; for ($i = 6; $i >= 0; $i--) { $日期 = date('Y-m-d', strtotime("-{$i} days")); $st = $db->prepare("SELECT COUNT(*) as 次数 FROM `{$表}` WHERE `章`='AI请求记录' AND `户`=? AND `时` LIKE ?"); $st->execute([$用户, $日期 . '%']); $行 = $st->fetch(); $趋势[] = ['日期' => $日期, '次数' => intval($行['次数'])]; } $统计['7日趋势'] = $趋势; return $统计; } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第九部分:对话管理 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ /** 获取对话列表 */ function 获取对话列表() { 需要登录(); $用户 = $_SESSION['用户id']; $结果 = 查询( ['章' => '对话', '户' => $用户, '隐' => ['<>', '1']], '`排` DESC, `号` DESC', 200 ); $列表 = []; foreach ($结果 as $行) { $列表[] = [ 'id' => $行['号'], '标题' => $行['题'] ?: '新对话', '模型' => $行['节'], '时间' => $行['时'], '置顶' => $行['排'] === '999999', '项目' => $行['组'], ]; } return $列表; } /** 获取对话消息 */ function 获取对话消息($对话id) { 需要登录(); $记录 = 查询(['章' => '对话消息', '组' => strval($对话id), '户' => $_SESSION['用户id']], '`号` ASC', 1000); $消息 = []; foreach ($记录 as $行) { $消息[] = [ 'id' => $行['号'], '角色' => $行['节'], '内容' => $行['文'], '模型' => $行['签'], '时间' => $行['时'], ]; } return $消息; } /** 创建新对话 */ function 创建对话($标题 = '新对话', $模型 = 'deepseek', $项目 = '') { 需要登录(); $id = 插入([ '章' => '对话', '节' => $模型, '题' => $标题, '组' => $项目, '排' => '0', '隐' => '0', '户' => $_SESSION['用户id'], '时' => 现在(), '群' => '对话', ]); return $id; } /** 保存对话消息 */ function 保存对话消息($对话id, $角色, $内容, $模型 = '') { 需要登录(); // 更新对话标题(用第一条用户消息) if ($角色 === 'user') { $对话 = 查询(['号' => $对话id], '`号` DESC', 1); if (!empty($对话) && ($对话[0]['题'] === '新对话' || empty($对话[0]['题']))) { $新标题 = mb_substr($内容, 0, 50); 更新($对话id, ['题' => $新标题]); } } return 插入([ '章' => '对话消息', '节' => $角色, '文' => $内容, '签' => $模型, '组' => strval($对话id), '户' => $_SESSION['用户id'], '时' => 现在(), '群' => '消息', ]); } /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第十部分:API路由 ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ $动作 = 取参('动'); if (!empty($动作)) { // 处理微信服务器验证 if ($动作 === '微信验证') { if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['echostr'])) { 微信验证(); } else { 微信事件处理(); } } // 初始化数据库连接 连接数据库(); switch ($动作) { // ── 登录相关 ── case '生成二维码': $结果 = 生成登录二维码(); 响应($结果); break; case '检查登录': $场景 = 取参('场景'); $结果 = 检查登录状态($场景); 响应($结果); break; case '调试登录': 调试登录(); break; case '登录状态': if (!empty($_SESSION['用户id'])) { 响应([ '已登录' => true, '用户名' => $_SESSION['用户名'] ?? '', '头像' => $_SESSION['头像'] ?? '', '用户id' => $_SESSION['用户id'], ]); } else { 响应(['已登录' => false]); } break; case '退出登录': session_destroy(); 响应(['成功' => true]); break; // ── AI请求 ── case 'AI请求': AI流式请求(); break; // ── 对话管理 ── case '对话列表': 需要登录(); 响应(['列表' => 获取对话列表()]); break; case '对话消息': 需要登录(); $id = intval(取参('id')); 响应(['消息' => 获取对话消息($id)]); break; case '创建对话': 需要登录(); $数据 = 取JSON(); $id = 创建对话($数据['标题'] ?? '新对话', $数据['模型'] ?? 'deepseek', $数据['项目'] ?? ''); 响应(['id' => $id]); break; case '保存消息': 需要登录(); $数据 = 取JSON(); $id = 保存对话消息($数据['对话id'], $数据['角色'], $数据['内容'], $数据['模型'] ?? ''); 响应(['id' => $id]); break; case '删除对话': 需要登录(); $id = intval(取参('id')); 更新($id, ['隐' => '1']); 记录日志('删除', '删除对话', $id); 响应(['成功' => true]); break; case '置顶对话': 需要登录(); $id = intval(取参('id')); $对话 = 查询(['号' => $id], '', 1); if (!empty($对话)) { $新排 = $对话[0]['排'] === '999999' ? '0' : '999999'; 更新($id, ['排' => $新排]); } 响应(['成功' => true]); break; case '清空上下文': 需要登录(); $对话id = intval(取参('id')); $db = 连接数据库(); global $数据库配置; $st = $db->prepare("DELETE FROM `{$数据库配置['表名']}` WHERE `章`='对话消息' AND `组`=? AND `户`=?"); $st->execute([strval($对话id), $_SESSION['用户id']]); 响应(['成功' => true]); break; // ── 项目文件 ── case '项目列表': 需要登录(); 初始化目录(); 响应(['列表' => 获取项目列表()]); break; case '创建项目': 需要登录(); $数据 = 取JSON(); $名称 = preg_replace('/[^a-zA-Z0-9\-_\.]/', '', $数据['名称'] ?? ''); if (empty($名称)) 响应(['错误' => '项目名不能为空'], 400); 创建项目目录($名称); 记录日志('创建', '创建项目', $名称); 响应(['成功' => true]); break; case '文件树': 需要登录(); $项目 = 净化(取参('项目')); 响应(['树' => 获取文件树($项目)]); break; case '读取文件': 需要登录(); $路径 = 取参('路径'); $内容 = 读取文件($路径); if ($内容 === null) 响应(['错误' => '文件不存在'], 404); $扩展名 = pathinfo($路径, PATHINFO_EXTENSION); 响应(['内容' => $内容, '扩展名' => $扩展名, '路径' => $路径]); break; case '保存文件': 需要登录(); $数据 = 取JSON(); $路径 = $数据['路径'] ?? ''; $内容 = $数据['内容'] ?? ''; if (empty($路径)) 响应(['错误' => '路径不能为空'], 400); if (保存文件($路径, $内容)) { 记录日志('保存', '保存文件', $路径); 响应(['成功' => true]); } else { 响应(['错误' => '保存失败'], 500); } break; case '自动保存': 需要登录(); $数据 = 取JSON(); $项目 = $数据['项目'] ?? ''; $扩展名 = $数据['扩展名'] ?? 'html'; $内容 = $数据['内容'] ?? ''; if (empty($项目)) 响应(['错误' => '项目不能为空'], 400); $结果 = 自动编号保存($项目, $扩展名, $内容); if ($结果) { 记录日志('自动保存', '自动编号保存', $结果['路径']); 响应($结果); } else { 响应(['错误' => '保存失败'], 500); } break; case '删除文件': 需要登录(); $路径 = 取参('路径'); if (删除文件($路径)) { 记录日志('删除', '删除文件', $路径); 响应(['成功' => true]); } else { 响应(['错误' => '删除失败'], 500); } break; case '重命名文件': 需要登录(); $数据 = 取JSON(); global $根目录; $旧路径 = $根目录 . '/' . $数据['旧路径']; $新路径 = $根目录 . '/' . $数据['新路径']; if (安全路径(dirname($旧路径)) && 安全路径(dirname($新路径))) { rename($旧路径, $新路径); 记录日志('重命名', '重命名文件', $数据['旧路径'] . ' -> ' . $数据['新路径']); 响应(['成功' => true]); } else { 响应(['错误' => '路径不安全'], 403); } break; case '上传文件': 需要登录(); if (empty($_FILES['文件'])) 响应(['错误' => '没有文件'], 400); $文件 = $_FILES['文件']; $项目 = 净化($_POST['项目'] ?? ''); global $系统配置, $根目录; if ($文件['size'] > $系统配置['上传限制']) 响应(['错误' => '文件过大'], 400); $扩展名 = strtolower(pathinfo($文件['name'], PATHINFO_EXTENSION)); if (!in_array($扩展名, $系统配置['允许扩展名'])) 响应(['错误' => '不允许的文件类型'], 400); $目标目录 = $根目录 . '/' . $项目 . '/' . $扩展名; if (!is_dir($目标目录)) mkdir($目标目录, 0755, true); $目标路径 = $目标目录 . '/' . basename($文件['name']); if (move_uploaded_file($文件['tmp_name'], $目标路径)) { 记录日志('上传', '上传文件', $项目 . '/' . $扩展名 . '/' . basename($文件['name'])); 响应(['成功' => true, '路径' => $项目 . '/' . $扩展名 . '/' . basename($文件['name'])]); } else { 响应(['错误' => '上传失败'], 500); } break; // ── 设置 ── case '获取设置': $设置 = 获取设置(); // 获取AI密钥配置状态(不返回密钥本身) $密钥状态 = []; global $AI模型表; foreach ($AI模型表 as $id => $模型) { $key = 获取AI密钥($id); $密钥状态[$id] = ['已配置' => !empty($key), '名称' => $模型['名称']]; } 响应(['设置' => $设置, '密钥状态' => $密钥状态]); break; case '保存设置': $数据 = 取JSON(); 保存设置($数据); 响应(['成功' => true]); break; case '保存密钥': 需要登录(); $数据 = 取JSON(); $模型 = $数据['模型'] ?? ''; $密钥 = $数据['密钥'] ?? ''; if (empty($模型)) 响应(['错误' => '模型不能为空'], 400); 保存AI密钥($模型, $密钥); 记录日志('设置', '保存AI密钥', $模型); 响应(['成功' => true]); break; // ── 统计 ── case '统计数据': $统计 = 获取统计数据(); 响应($统计); break; // ── 备份 ── case '备份数据': 需要登录(); $db = 连接数据库(); global $数据库配置; $st = $db->query("SELECT * FROM `{$数据库配置['表名']}` WHERE `户`='" . $_SESSION['用户id'] . "'"); $数据 = $st->fetchAll(); header('Content-Type: application/json'); header('Content-Disposition: attachment; filename=asipk_backup_' . date('YmdHis') . '.json'); echo json_encode($数据, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; break; default: 响应(['错误' => '未知操作'], 400); } exit; } // 初始化目录结构 连接数据库(); 初始化目录(); $csrf_token = 生成CSRF(); /* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ 第十一部分:前端页面(单文件HTML+CSS+JS 完全内嵌于PHP中) ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */ ?> asipk - AI智能开发环境

🚀 asipk

AI智能集成开发环境

正在生成二维码...
请用微信扫码登录

📁 文件

💬 对话

🚀

欢迎使用 asipk

选择AI模型,开始对话。输入代码需求,生成/编辑/分析代码。

0 字
👁 预览

💡 提示词

常用提示词

⚙ 设置

🔑 AI模型密钥配置

🎨 界面设置

切换亮色/暗色主题
编辑器和对话字体大小

🤖 AI设置

携带最近N条消息
0-2,越高越随机
📊 统计中心

7日请求趋势