// server.js const express = require("express"); const multer = require("multer"); const cors = require("cors"); const fs = require("fs"); const path = require("path"); const OpenAI = require("openai"); const bodyParser = require("body-parser"); const app = express(); const port = 30009; const mysql = require("mysql2/promise"); // 初始化数据库连接池 const pool = mysql.createPool({ host: "1.95.137.212", user: "fastbee", password: "maibu520", database: "fastbee", waitForConnections: true, connectionLimit: 10, }); // 允许大文件请求体 app.use(bodyParser.json({ limit: "500mb" })); app.use(bodyParser.urlencoded({ limit: "500mb", extended: true })); // Multer 上传目录 const upload = multer({ dest: "uploads/", limits: { fileSize: 500 * 1024 * 1024 }, // 500MB }); // Node <20 兼容 File if (typeof File === "undefined") { globalThis.File = require("node:buffer").File; } // 启用 CORS app.use(cors()); app.use(express.json({ limit: "200mb" })); // 支持大 JSON app.use(express.urlencoded({ extended: true })); // 支持 form-data 字段 // 初始化混元客户端 // const client = new OpenAI({ // apiKey: "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", // baseURL: "https://openrouter.ai/api/v1", // timeout: 120000, // }); // const client = new OpenAI({ // baseURL:"https://openrouter.ai/api/v1", // apiKey:"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", // timeout: 120000 // }); // const client = new OpenAI({ // baseURL:"https://satabot.openai.azure.com/", // apiKey:"DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", // timeout: 120000 // }); //https://satabot.openai.azure.com/ // const client = new OpenAI({ // apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", // 推荐放环境变量 // baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-4.1/chat/completions?api-version=2025-01-01-preview", // defaultQuery: { "api-version": "2024-02-15-preview" }, // defaultHeaders: { "api-key": process.env.AZURE_OPENAI_KEY }, // timeout: 120000 // }); // https://satabot.openai.azure.com/openai/deployments/gpt-4.1/ // https://satabot.openai.azure.com/openai/deployments/gpt-5-mini/ // 初始化客户端 const client = new OpenAI({ apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-4.1", defaultQuery: { "api-version": "2024-04-01-preview" }, timeout: 120000 }); const client1 = new OpenAI({ apiKey: "DxvRiQWROzLTapiSHcK0FXKAAXDBlhci1XaqTTpr50sgUsAkmxfUJQQJ99BIACYeBjFXJ3w3AAABACOGZdHp", baseURL: "https://satabot.openai.azure.com/openai/deployments/gpt-5-mini/", defaultQuery: { "api-version": "2024-04-01-preview" }, timeout: 120000 }); app.get("/test-db", async (req, res) => { try { await pool.query( "INSERT INTO ai_chat_messages (session_id, role, content) VALUES (?, 'user', ?)", ["sess1", "hello"] ); res.send("OK"); } catch (err) { console.error(err); res.send("FAIL"); } }); // 查询用户所有会话列表 app.get("/sessions", async (req, res) => { console.log("收到的 userId:", req.query.userId); try { const userId = req.query.userId; if (!userId || userId === "NaN" || isNaN(Number(userId))) { console.log("userId:", req.query.userId); res.json({ code: 300, error: "http://1.95.137.212/#/login?goRemote=1" }); return; // return res.status(500).json({ code: 505, error: "http://1.95.137.212/#/login?redirect=%2Findex&goRemote=1" }); // return res.redirect('http://1.95.137.212/#/login?redirect=%2Findex&goRemote=1'); } // return; // 查该用户所有的会话,按最后活跃时间倒序 const [rows] = await pool.query( ` SELECT session_id, MAX(created_at) AS last_time FROM ai_chat_messages WHERE user_id = ? GROUP BY session_id ORDER BY last_time DESC `, [userId] ); res.json({ code: 0, userId, sessions: rows.map((r) => r.session_id), // 只返回 session_id 列表 }); } catch (err) { console.error("查询会话列表失败:", err); res.status(500).json({ code: 1, error: err.message }); } }); // 查询会话历史 app.get("/history", async (req, res) => { try { const { sessionId } = req.query; if (!sessionId) { return res.status(400).json({ code: 1, error: "sessionId 必填" }); } // 查询数据库 const [rows] = await pool.query( "SELECT user_id, role, content, image_base64, created_at FROM ai_chat_messages WHERE session_id = ? ORDER BY id ASC", [sessionId] ); res.json({ code: 0, sessionId, messages: rows, }); } catch (err) { console.error("查询会话历史失败:", err); res.status(500).json({ code: 1, error: err.message }); } }); // ====================== API ====================== app.post("/analyze", upload.single("file"), async (req, res) => { try { res.setHeader("Content-Type", "text/plain; charset=utf-8"); res.setHeader("Transfer-Encoding", "chunked"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); const { userPrompt, imageBase64, userId, sessionId } = req.body; // 如果前端没有传 sessionId,生成一个 const sessId = sessionId || Date.now().toString(); // ------------------ 1️⃣ 保存用户提问 ------------------ await pool.query( "INSERT INTO ai_chat_messages (session_id, user_id, role, content, image_base64) VALUES (?, ?, 'user', ?, ?)", [sessId, userId || null, userPrompt || "", imageBase64 || null] ); // ------------------ 2️⃣ 图像分析 ------------------ const completion = await client.chat.completions.create({ model: "gpt-4.1", stream: true, // do_scene: true, // do_behavior: true, // llm_model: "hunyuan-turbos-latest", messages: [ { role: "system", content: "", }, { role: "user", content: [ { type: "text", text: userPrompt || "请描述图片内容。" }, imageBase64 ? { type: "image_url", image_url: { url: imageBase64.startsWith("data:") ? imageBase64 : `data:image/png;base64,${imageBase64}`, }, } : null, ].filter(Boolean), }, ], }); let sceneText = ""; for await (const chunk of completion) { const delta = chunk.choices[0]?.delta?.content || ""; sceneText += delta; if (delta) res.write( `data: ${JSON.stringify({ type: "scene", content: delta })}\n\n` ); } // 保存图像分析结果到数据库 await pool.query( "INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)", [sessId, userId || null, sceneText] ); // ------------------ 3️⃣ LLM 分析 ------------------ const llmStream = await client1.chat.completions.create({ model: "gpt-5-mini", stream: true, messages: [ { role: "system", content:"" // content: "你是一名新能源项目的图像分析AI分析师。", }, { role: "user", content: `${sceneText}`, // content: `请基于以下场景分析结果提供部署建议或优化意见:\n${sceneText}`, }, ], }); let llmText = ""; for await (const chunk of llmStream) { const delta = chunk.choices[0]?.delta?.content || ""; llmText += delta; if (delta) res.write( `data: ${JSON.stringify({ type: "llm", content: delta })}\n\n` ); } // 保存 LLM 输出到数据库 await pool.query( "INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)", [sessId, userId || null, llmText] ); res.write("data: [DONE]\n\n"); res.end(); // 删除上传文件 if (req.file && req.file.path) { fs.unlinkSync(req.file.path); } } catch (err) { console.error("❌ 调用失败:", err.response?.data || err.message); res.status(500).json({ code: 1, error: err.response?.data || err.message }); } }); // 静态文件服务 app.use("/uploads", express.static("uploads")); // 启动服务 app.listen(port, () => { console.log(`✅ 服务器运行:http://localhost:${port}`); });