// 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 xml2js = require('xml2js'); const xmlparser = require('express-xml-bodyparser'); // 引入XML解析中间件 const bodyParser = require("body-parser"); const axios = require('axios'); const crypto = require('crypto'); require('dotenv').config(); const app = express(); const port =30009; const appid = process.env.WECHAT_APPID; const secret = process.env.WECHAT_SECRET; const accessTokenUrl = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`; 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 })); app.use(xmlparser({ explicitArray: false, // 禁用默认的数组格式,和你后续xml2js配置一致 trim: 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(cors({ // origin: ["https://ai.satabot.com"], // 允许的域 // methods: "GET,POST,PUT,DELETE,OPTIONS", // allowedHeaders: "Content-Type,Authorization", // credentials: true // })); // app.options('*', 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 }); // 获取 access_token // getAccessToken(); async function getAccessToken() { const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`; const res = await axios.get(url); if (res.data.access_token) { console.log('✅ 获取 access_token 成功',res.data); return res.data.access_token; } else { throw new Error('❌ 获取 access_token 失败: ' + JSON.stringify(res.data)); } } const WECHAT_OAUTH_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token'; app.get('/wechat/oauth/access-token', async (req, res) => { try { console.log(req.query) // 1. 接收并校验前端传递的 code 参数 const { code } = req.query; // 从URL查询参数中获取 code if (!code) { return res.status(400).json({ code: -1, message: '参数错误:缺少 code(微信授权临时票据)' }); } // 3. 调用微信 OAuth2 接口,传递必要参数 const response = await axios.get(WECHAT_OAUTH_TOKEN_URL, { params: { appid: "wx364b8ebf17fe45fe", // 你的微信appid secret: "9b7476020991a2c71f1416da7f011d31", // 你的微信secret code: code, // 前端传递的授权code grant_type: 'authorization_code' // 固定值 } }); // 4. 处理微信返回的结果 const wechatData = response.data; // 微信接口失败(返回 errcode 字段) if (wechatData.errcode) { throw new Error(`微信接口错误:${wechatData.errmsg}(错误码:${wechatData.errcode})`); } // 5. 成功响应:返回微信返回的所有信息(包含 access_token、openid 等) res.status(200).json({ code: 0, message: '获取 OAuth2 access_token 成功', data: wechatData // 包含 access_token、expires_in、refresh_token、openid 等 }); } catch (error) { // 异常处理:捕获网络错误、微信接口错误等 console.error('获取 OAuth2 access_token 失败:', error.message); res.status(500).json({ code: -3, message: '获取 access_token 失败:' + error.message }); } }); // 创建菜单的接口 app.post('/create-menu', async (req, res) => { const accessToken = await getAccessToken(); const menuData = getMenuForPlatform(req); // 根据请求判断平台并获取菜单 const createMenuUrl = `https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${accessToken}`; try { const response = await axios.post(createMenuUrl, menuData); res.send(`菜单创建成功: ${JSON.stringify(response.data)}`); } catch (error) { res.status(500).send(`菜单创建失败: ${error}`); } }); app.post('/wechat', async (req, res) => { try { console.log("收到请求:", req.body); const xml = req.body; const message = xml.xml; // 修复语法错误:移除msgtype前的空格 const msgType = message.msgtype; const fromUser = message.fromusername; // openid const toUser = message.tousername; // 公众号原始ID console.log("消息类型:", msgType); console.log("用户OpenID:", fromUser); // 菜单点击事件 if (msgType === 'event' && message.event === 'CLICK') { const key = message.eventkey; console.log("菜单点击Key:", key); if (key === 'AI_INTELLIGENT') { try { const timestamp = Date.now(); // 方案一:简单文本回复(推荐,避免超时) const reply = ` ${Date.now()} 立即进入AI智能体]]> `; res.send(reply); return; // 启动异步任务处理耗时操作 setTimeout(async () => { try { const token = await getAccessToken(); console.log("获取到Token:", token); // 获取用户信息 const userRes = await axios.get('https://api.weixin.qq.com/sns/userinfo', { params: { access_token:token, fromUser, lang: 'zh_CN' } }); console.log(userRes, 'user000000000000'); // res.redirect('https://ai.satabot.com'); const { nickname, errcode: userErrCode } = userRes.data; console.log(nickname, 'nickname'); return; if (userErrCode) return res.status(500).send(`获取用户信息失败:${userErrCode}`); const userInfoRes = await axios.get( `https://api.weixin.qq.com/cgi-bin/user/info?access_token=${token}&openid=${fromUser}&lang=zh_CN` ); const user = userInfoRes.data; console.log("用户信息:", user); // 使用客服消息接口发送详细回复 if (user && user.nickname) { await sendCustomerServiceMessage(fromUser, `你好,${user.nickname}!欢迎使用AI智能体`); } } catch (err) { console.error("异步处理错误:", err); } }, 0); return; // 方案二:直接回复(如果能确保5秒内完成) /* const token = await getAccessToken(); console.log("Token:", token); const userInfoRes = await axios.get( `https://api.weixin.qq.com/cgi-bin/user/info?access_token=${token}&openid=${fromUser}&lang=zh_CN` ); const user = userInfoRes.data; console.log("用户信息:", user); const reply = ` ${Date.now()} `; res.send(reply); return; */ } catch (err) { console.error("处理AI_INTELLIGENT错误:", err); // 出错时返回标准回复 const errorReply = ` ${Date.now()} `; res.send(errorReply); return; } } } // 默认回复空串(表示不处理) res.send(''); } catch (err) { console.error("服务器错误:", err); // 异常情况下回复空串 res.send(''); } }); // 新增:发送客服消息的函数 async function sendCustomerServiceMessage(openid, content) { try { const token = await getAccessToken(); const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${token}`; const data = { touser: openid, msgtype: 'text', text: { content: content } }; await axios.post(url, data, { headers: { 'Content-Type': 'application/json' } }); console.log("客服消息发送成功"); } catch (err) { console.error("发送客服消息失败:", err); } } // app.post('/wechat', async (req, res) => { // try { // console.log(req,"收到恢复") // const xml = req.body; // console.log(xml); // const message = xml.xml; // const msgType = message.msgtype; // const fromUser = message.fromusername; // openid // // 菜单点击事件 // if (msgType === 'event' && message.event === 'CLICK') { // const key = message.eventkey; // if (key === 'AI_INTELLIGENT') { // const redirectUri = encodeURIComponent(`https://ai.satabot.com/api/wechat/oauth/callback`); // const state = fromUser; // 可用 openid 做状态标识 // console.log(state,"state") // // const oauthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`; // // console.log("oauthUrl",oauthUrl) // // // 回复用户点击菜单提示,引导跳转 // // const reply = ` // // // // // // // // ${Date.now()} // // // // // // // // `; // // res.send(reply); // // return; // const token = await getAccessToken(); // console.log(token,"token") // // 获取用户信息 // // https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=o6_bmjrPTlm6_2sgVt7hMZOPfL2M&lang=zh_CN // const userInfoRes = await axios.get(`https://api.weixin.qq.com/cgi-bin/user/info?access_token=${token}&openid=${fromUser}&lang=zh_CN`); // // https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=ENCODED_URL&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect // const user = userInfoRes.data; // console.log(userInfoRes,'用户信息') // // 回复文本消息 // const reply = ` // // // // ${Date.now()} // // // // `; // res.send(xml); // return; // } // } // // 默认回复空 // res.send('success'); // } catch (err) { // console.error(err); // res.send('success'); // } // }); // OAuth2 回调 app.get('/wechat/oauth/callback', async (req, res) => { try { const { code, state } = req.query; console.log(code,"code") // 用 code 换取 access_token 和 openid const tokenResp = await axios.get( `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appid}&secret=${secret}&code=${code}&grant_type=authorization_code` ); const { access_token, openid } = tokenResp.data; // 拉取用户信息 const userInfoResp = await axios.get( `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}&lang=zh_CN` ); const user = userInfoResp.data; console.log(user, "获取到用户信息"); // 你可以直接跳转到前端页面并带上用户信息 res.redirect(`https://ai.satabot.com?nickname=${encodeURIComponent(user.nickname)}&openid=${openid}`); } catch (err) { console.error(err); res.send("获取用户信息失败"); } }); 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("/wechat/userinfo", async (req, res) => { const { code } = req.query; if (!code) return res.status(400).send("缺少 code"); try { // 1. 用 code 换取 access_token 和 openid const tokenResp = await axios.get( `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appid}&secret=${secret}&code=${code}&grant_type=authorization_code` ); const { access_token, openid } = tokenResp.data; // 2. 拉取用户信息 const userRes = await axios.get( `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}&lang=zh_CN` ); const userInfo = userRes.data; // 3. 返回给前端 res.json({ code: 0, data: userInfo }); } catch (err) { console.error(err); res.status(500).send("获取用户信息失败"); } }); app.post("/user", async (req, res) => { try { const { userId } = req.body; // 从请求体里拿到 user_id if (!userId&&userId!="NaN") { return res.status(400).send("缺少 user_id"); } // 查询 user_name const [rows] = await pool.query( "SELECT user_name FROM sys_user WHERE user_id = ?", [userId] ); if (rows.length === 0) { res.json({ code: 300, error: "http://ri.satabot.com/#/login?goRemote=1", msg:"未找到该用户" }); return; } res.json({ code:0,userId, userName: rows[0].user_name }); } catch (err) { console.error(err); res.status(500).send("服务器错误"); } }); app.post("/getuserByopenid", async (req, res) => { try { const { userId } = req.body; // 从请求体里拿到 user_id if (!userId) { return res.status(400).send("缺少 user_id"); } // 查询 user_name const [rows] = await pool.query( "SELECT * FROM sys_user WHERE open_id = ?", [userId] ); if (rows.length > 0) { // 用户存在,直接返回 res.json({ code: 0, msg: "找到该用户",userId:rows[0].user_id }); return } else { // 用户不存在,插入新用户 const [result] = await pool.query( "INSERT INTO sys_user (open_id,user_name,nick_name) VALUES (?,?,?)", [userId,userId,userId] ); console.log(result,"udgwehvdh") res.json({code:0,msg:"新增该用户",userId:result.insertId}); return ; } } catch (err) { console.error(err); res.status(500).send("服务器错误"); } }); const TOKEN = 'my_token_123'; // 与公众号后台配置一致 app.get('/wechat', (req, res) => { const { signature, timestamp, nonce, echostr } = req.query; console.log(req, "req"); const arr = [TOKEN, timestamp, nonce].sort(); const str = arr.join(''); const hash = crypto.createHash('sha1').update(str).digest('hex'); if (hash === signature) res.send(echostr); else res.send('验证失败'); }); // 查询用户所有会话列表 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:'https://gsgg.com' // error: "http://ri.satabot.com/#/dhhdghgbshwadhg" error: "http://ri.satabot.com/#/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,title,MAX(created_at) AS last_time // FROM ai_chat_messages // WHERE user_id = ? // GROUP BY session_id // ORDER BY last_time DESC // `, // [userId] // ); const [rows] = await pool.query(` SELECT m.session_id, m.title FROM ai_chat_messages m INNER JOIN ( SELECT session_id, MAX(created_at) AS last_time FROM ai_chat_messages WHERE user_id = ? GROUP BY session_id ) t ON m.session_id = t.session_id AND m.created_at = t.last_time WHERE m.user_id = ? ORDER BY t.last_time ASC `, [userId, userId]); res.json({ code: 0, userId, sessions: rows.map((r) => ({ sessId: r.session_id, title: r.title })), // 只返回 session_id 列表 }); } catch (err) { console.error("查询会话列表失败:", err); res.status(500).json({ code: 1, error: err.message }); } }); // 查询会话历史 app.get("/history", async (req, res) => { try { if(!req.query?.sessionId || req.query?.sessionId == "NaN") { return res.status(400).json({ code: 1, error: "sessionId 必填" }); } const { sessionId } = req.query; // 查询数据库 const [rows] = await pool.query( "SELECT user_id, role, content, image_base64, created_at,title 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 }); } }); // 删除指定会话及其消息 app.delete("/session", async (req, res) => { try { console.log("会话删除:", req.query); // if (!sessionId || !userId) if (req.query?.userId == "NaN" || !req.query?.userId || !req.query?.sessionId || req.query?.sessionId == "NaN") { return res.status(400).json({ code: 1, error: "sessionId 和 userId 必填" }); } const { sessionId, userId } = req.query; // 确保只能删除自己的会话 const [rows] = await pool.query( "SELECT COUNT(*) as cnt FROM ai_chat_messages WHERE session_id = ? AND user_id = ?", [sessionId, userId] ); if (rows[0].cnt === 0) { return res.status(404).json({ code: 1, error: "会话不存在或无权限" }); } // 删除该 session 的所有消息 await pool.query( "DELETE FROM ai_chat_messages WHERE session_id = ? AND user_id = ?", [sessionId, userId] ); res.json({ code: 0, message: `会话 ${sessionId} 已删除`, }); } catch (err) { console.error("删除会话失败:", err); res.status(500).json({ code: 1, error: err.message }); } }); // 新建会话(实际上就是往消息表里插入一条记录) app.post("/session", async (req, res) => { try { console.log("新建会话: ",req.body) if (req.body?.userId == "NaN" || !req.body?.userId || !req.body?.sessionId || req.body?.sessionId == "NaN") { console.log("=== userId: ",req.body?.userId) return res.status(400).json({ code: 1, error: "userId 和 sessionId 必填" }); } const { userId, sessionId } = req.body; // 插入一条“新会话”的首条消息,可以是空内容 await pool.query( "INSERT INTO ai_chat_messages (user_id, session_id, role, content) VALUES (?, ?, 'system', '')", [userId, sessionId] ); res.json({ code: 0, message: `会话 ${sessionId} 已创建`, }); } catch (err) { console.error("创建会话失败:", err); res.status(500).json({ code: 1, error: err.message }); } }); const stopFlags = new Map(); // ====================== API ====================== app.post("/analyze", upload.single("file"), async (req, res) => { try { const { requestId } = req.body; 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"); let aborted = false; req.on("close", () => { console.log("🔴 前端断开连接,停止整个分析流程"); aborted = true; }); const logBody = { ...req.body }; if(req.body?.imageBase64) { delete logBody.imageBase64; } console.log("analyze: ", logBody); // console.log("analyze: ",req.body) if (!req.body?.userId || !req.body?.sessionId) { console.log("缺少必要参数"); return res.status(400).json({ error: "缺少必要参数" }); } const { userPrompt, imageBase64, userId, sessionId } = req.body; // 如果前端没有传 sessionId,生成一个 const sessId = sessionId || Date.now().toString(); // ------------------ 获取历史对话(上下文) ------------------ const [historyRows] = await pool.query( "SELECT role, content, image_base64 FROM ai_chat_messages WHERE session_id = ? AND user_id = ? ORDER BY created_at ASC", [sessId, userId || null] ); // 构建历史消息数组 const historyMessages = historyRows.map(row => { const message = { role: row.role, content: [] }; // 添加文本内容 if (row.content) { message.content.push({ type: "text", text: row.content }); } // 添加图片内容 if (row.image_base64) { message.content.push({ type: "image_url", image_url: { url: row.image_base64.startsWith("data:") ? row.image_base64 : `data:image/png;base64,${row.image_base64}`, } }); } return message; }); // ------------------ 保存用户提问 ------------------ await pool.query( "INSERT INTO ai_chat_messages (session_id, user_id, role, content, image_base64) VALUES (?, ?, 'user', ?, ?)", [sessId, userId || null, userPrompt || "", imageBase64 || null] ); // console.log("userPrompt: ",userPrompt); // console.log("imageBase64: ",imageBase64); // console.log("userId: ",userId); // console.log("sessionId: ",sessionId); // ------------------ 图像分析 ------------------ // 构建包含上下文的消息数组 const imageAnalysisMessages = [ { role: "system", content: "你是一名图像分析专家,需要结合历史对话内容分析当前图像。" }, ...historyMessages, // 添加历史对话作为上下文 { 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), }, ]; const completion = await client.chat.completions.create({ model: "gpt-4.1", stream: true, messages: imageAnalysisMessages }); console.log("====== 开始图像分析... ======") let sceneText = ""; for await (const chunk of completion) { console.log(stopFlags.get(requestId),"停止了嘛") if (stopFlags.get(requestId)) { console.log("⛔ 图像分析被前端中断"); res.end(); stopFlags.delete(requestId); // 用完清理 return; } const delta = chunk.choices[0]?.delta?.content || ""; sceneText += delta; // if (delta) // res.write( // `data: ${JSON.stringify({ type: "scene", content: delta })}\n\n` // ); if (delta) { for (const ch of delta) { res.write(`data: ${JSON.stringify({ type: "scene", content: ch })}\n\n`); await new Promise(r => setTimeout(r, 0)); // 控制渲染节奏 } } } console.log("====== 图像分析结束 ======") // 保存图像分析结果到数据库 // await pool.query( // "INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)", // [sessId, userId || null, sceneText] // ); // ------------------ 3️⃣ LLM 分析 ------------------ // console.log("LLM 分析 sceneText: ", sceneText) // const preText = "详细分析这个光伏场站,根据图片给出详细的运维方案" // + "如果这个光伏场站需要割草怎么做,给出一份详细的计划" // + "如果这个光伏场站需要用无人机清洗怎么做,给出一份详细的计划" // + "如果这个光伏场站需要巡检怎么做,给出一份详细的计划" // 构建包含完整上下文的LLM消息数组 const llmMessages = [ { role: "system", content: "你是一名新能源项目的图像分析AI分析师,需要结合历史对话提供专业建议。" }, ...historyMessages, // 添加历史对话作为上下文 { role: "user", content: userPrompt || "请基于图片分析提供建议。" }, { role: "assistant", content: sceneText // 添加当前图像分析结果 } ]; const llmStream = await client1.chat.completions.create({ model: "gpt-5-mini", stream: true, messages: llmMessages }); console.log("====== 开始深度思考... ======") let llmText = ""; for await (const chunk of llmStream) { if (stopFlags.get(requestId)) { console.log("⛔ 深度分析被前端中断"); res.end(); stopFlags.delete(requestId); return; } const delta = chunk.choices[0]?.delta?.content || ""; llmText += delta; // if (delta) // res.write( // `data: ${JSON.stringify({ type: "llm", content: delta})}\n\n` // ); if (delta) { for (const ch of delta) { res.write(`data: ${JSON.stringify({ type: "llm", content: ch })}\n\n`); await new Promise(r => setTimeout(r, 0)); // 控制渲染节奏 } } } // 保存 LLM 输出到数据库 try { const titlePrompt = `请根据以下对话内容生成一个简短的标题(不超过15字): 用户问题: ${userPrompt} AI回答: ${llmText}`; const titleResp = await client1.chat.completions.create({ model: "gpt-5-mini", messages: [ { role: "system", content: "你是标题生成助手,只返回标题,不要解释。" }, { role: "user", content: titlePrompt } ] }); const title = titleResp.choices[0]?.message?.content?.trim() || "未命名会话"; res.write( `data: ${JSON.stringify({ type: "title", content: title })}\n\n` ); console.log("生成的标题:", title); // 保存到数据库(建议你给 ai_chat_messages 表新增一列 title,或者单独建一张会话表) await pool.query( "INSERT INTO ai_chat_messages (session_id, user_id, role, content,title) VALUES (?, ?, 'assistant', ?,?)", [sessId, userId || null, llmText,title] ); } catch (err) { console.error("生成标题失败:", err.message); } res.write("data: [DONE]\n\n"); res.end(); // 删除上传文件 if (req.file && req.file.path) { fs.unlinkSync(req.file.path); } console.log("====== 分析完成 ======") } catch (err) { console.error("❌ 调用失败:", err.response?.data || err.message); res.status(500).json({ code: 1, error: err.response?.data || err.message }); } }); app.post("/stop", express.json(), (req, res) => { const { requestId } = req.body; console.log("🔴 收到停止信号:", requestId); stopFlags.set(requestId, true); console.log(stopFlags,"消息id"); res.json({ ok: true }); }); app.post('/submit',async (req, res) => { const { company, scenes, needs, userId } = req.body; console.log("收到数据:", company, scenes, needs, userId); if (!company || !needs || !userId) { return res.status(400).json({ message: "缺少必要字段" }); } try { const sql = "INSERT INTO pv_form (company, scenes, needs, userId) VALUES (?, ?, ?, ?)"; await pool.query(sql, [company, scenes, needs, userId || null]); res.json({ code:200,message: "提交成功!" }); } catch (err) { console.error("数据库插入失败:", err); res.status(500).json({ message: "数据库写入失败" }); } }); // // 获取微信授权回调接口 // app.get("/callback", async (req, res) => { // const code = req.query.code; // try { // // 用 code 换 openid // const resp = await axios.get("https://api.weixin.qq.com/sns/oauth2/access_token", { // params: { // appid: APPID, // secret: APPSECRET, // code: code, // grant_type: "authorization_code", // }, // }); // const data = resp.data; // console.log("微信授权返回:", data); // if (data.openid) { // res.json({ // code: 0, // openid:data.openid // }) // // 判断数据库里是否已经有这个 openid // // 没有 → 注册;有 → 登录 // // res.send(`授权成功,openid: ${data.openid}`); // } else { // res.json({ // code: 500, // msg:"微信授权失败"+ JSON.stringify(data) // }) // // res.send("授权失败: " + JSON.stringify(data)); // } // } catch (err) { // console.error(err); // res.send("服务器错误"); // } // }); // 静态文件服务 app.use("/uploads", express.static("uploads")); // 启动服务 app.listen(port, () => { console.log(`✅ 服务器运行:http://localhost:${port}`); });