687 lines
20 KiB
JavaScript
Executable File
687 lines
20 KiB
JavaScript
Executable File
// 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 axios = require('axios');
|
||
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 }));
|
||
|
||
// 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
|
||
});
|
||
|
||
|
||
|
||
// 公众号
|
||
function getMenuForPlatform(req) {
|
||
const userAgent = req.headers['user-agent'];
|
||
// 判断请求是否来自微信客户端
|
||
if (/MicroMessenger/i.test(userAgent)) {
|
||
// 微信菜单
|
||
return {
|
||
button: [
|
||
{
|
||
type: 'click',
|
||
name: '获取 OpenID',
|
||
key: 'GET_OPENID',
|
||
},
|
||
{
|
||
type: 'view',
|
||
name: '访问官网',
|
||
url: 'https://www.example.com',
|
||
sub_button: [ // 二级菜单
|
||
{
|
||
type: 'view',
|
||
name: '关于我们',
|
||
url: 'https://www.example.com/about',
|
||
},
|
||
{
|
||
type: 'view',
|
||
name: '联系我们',
|
||
url: 'https://www.example.com/contact',
|
||
}
|
||
]
|
||
}
|
||
]
|
||
};
|
||
}
|
||
}
|
||
|
||
// 获取 access_token
|
||
async function getAccessToken() {
|
||
try {
|
||
const res = await axios.get(accessTokenUrl);
|
||
return res.data.access_token;
|
||
} catch (error) {
|
||
console.error('获取 access_token 失败:', error);
|
||
}
|
||
}
|
||
|
||
// 创建菜单的接口
|
||
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', (req, res) => {
|
||
const xmlData = req.body.xml;
|
||
|
||
if (xmlData.Event === 'CLICK' && xmlData.EventKey === 'GET_OPENID') {
|
||
const openid = xmlData.FromUserName;
|
||
console.log(`用户的 openid: ${openid}`);
|
||
|
||
res.send(`<xml>
|
||
<ToUserName><![CDATA[${openid}]]></ToUserName>
|
||
<FromUserName><![CDATA[${xmlData.ToUserName}]]></FromUserName>
|
||
<CreateTime>${Date.now()}</CreateTime>
|
||
<MsgType><![CDATA[text]]></MsgType>
|
||
<Content><![CDATA[您的 OpenID 是:${openid}]]></Content>
|
||
</xml>`);
|
||
} else {
|
||
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) {
|
||
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) {
|
||
return res.status(404).send("未找到该用户");
|
||
}
|
||
|
||
res.json({ code:0,userId, userName: rows[0].user_name });
|
||
} catch (err) {
|
||
console.error(err);
|
||
res.status(500).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: "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 });
|
||
}
|
||
});
|
||
|
||
|
||
// ====================== 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 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)
|
||
{
|
||
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)
|
||
{
|
||
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.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}`);
|
||
});
|