补上丢掉的AI应答
This commit is contained in:
@@ -108,12 +108,14 @@ app.get('/api/rooms/:roomId/messages', auth, (req, res) => {
|
|||||||
res.json(msgs);
|
res.json(msgs);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 发送消息(统一入口)
|
// 发送消息(统一入口,并触发 AI)
|
||||||
app.post('/api/rooms/:roomId/messages', auth, (req, res) => {
|
app.post('/api/rooms/:roomId/messages', auth, async (req, res) => {
|
||||||
const { text, attachments } = req.body;
|
const { text, attachments } = req.body;
|
||||||
const roomId = req.params.roomId;
|
const roomId = req.params.roomId;
|
||||||
const username = req.user.username;
|
const username = req.user.username;
|
||||||
const timestamp = new Date().toLocaleString('zh-CN', { hour12: false });
|
const timestamp = new Date().toLocaleString('zh-CN', { hour12: false });
|
||||||
|
|
||||||
|
// 存储消息
|
||||||
const result = db.prepare('INSERT INTO messages (room_id, user, text, attachments, timestamp) VALUES (?,?,?,?,?)').run(
|
const result = db.prepare('INSERT INTO messages (room_id, user, text, attachments, timestamp) VALUES (?,?,?,?,?)').run(
|
||||||
roomId, username, text || '', attachments ? JSON.stringify(attachments) : null, timestamp
|
roomId, username, text || '', attachments ? JSON.stringify(attachments) : null, timestamp
|
||||||
);
|
);
|
||||||
@@ -134,7 +136,7 @@ app.post('/api/rooms/:roomId/messages', auth, (req, res) => {
|
|||||||
last_time: lastMsg ? lastMsg.timestamp : ''
|
last_time: lastMsg ? lastMsg.timestamp : ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// 广播给房间内其他成员,但不包括发送者自己
|
// 广播给房间内其他成员(排除自己)
|
||||||
broadcastToRoomExcludeSelf(roomId, username, {
|
broadcastToRoomExcludeSelf(roomId, username, {
|
||||||
type: 'new_message',
|
type: 'new_message',
|
||||||
message: msg,
|
message: msg,
|
||||||
@@ -144,8 +146,20 @@ app.post('/api/rooms/:roomId/messages', auth, (req, res) => {
|
|||||||
// 加入对话历史
|
// 加入对话历史
|
||||||
addToHistory(roomId, 'user', `${username}: ${text || '图片'}`);
|
addToHistory(roomId, 'user', `${username}: ${text || '图片'}`);
|
||||||
|
|
||||||
// 返回消息给发送者
|
// 先响应客户端
|
||||||
res.json(msg);
|
res.json(msg);
|
||||||
|
|
||||||
|
// 异步触发 AI 分析
|
||||||
|
try {
|
||||||
|
const history = getHistory(roomId);
|
||||||
|
const historyMessages = history.map(e => ({ role: e.role, content: e.content }));
|
||||||
|
const aiResponse = await analyzeWithDeepSeek(text || '', historyMessages, username);
|
||||||
|
if (aiResponse && aiResponse.action !== 'ignore') {
|
||||||
|
handleAIResult(aiResponse, roomId, username, text || '', attachments || []);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('AI 分析失败:', e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/upload', auth, upload.single('file'), (req, res) => {
|
app.post('/api/upload', auth, upload.single('file'), (req, res) => {
|
||||||
@@ -192,7 +206,7 @@ app.get('/api/rooms/:roomId/purchases/export', auth, (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// WebSocket 管理
|
// WebSocket 管理
|
||||||
const clients = new Map(); // ws -> { username, roomId }
|
const clients = new Map();
|
||||||
|
|
||||||
wss.on('connection', (ws, req) => {
|
wss.on('connection', (ws, req) => {
|
||||||
console.log('✅ WebSocket 客户端已连接');
|
console.log('✅ WebSocket 客户端已连接');
|
||||||
@@ -218,7 +232,7 @@ wss.on('connection', (ws, req) => {
|
|||||||
ws.roomId = msg.roomId;
|
ws.roomId = msg.roomId;
|
||||||
clients.set(ws, { username, roomId: msg.roomId });
|
clients.set(ws, { username, roomId: msg.roomId });
|
||||||
}
|
}
|
||||||
// 不再处理 message 类型,消息通过 HTTP API 发送
|
// 不再处理 message 类型
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -227,7 +241,6 @@ wss.on('connection', (ws, req) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 广播给房间内所有成员,排除指定用户
|
|
||||||
function broadcastToRoomExcludeSelf(roomId, excludeUsername, data) {
|
function broadcastToRoomExcludeSelf(roomId, excludeUsername, data) {
|
||||||
const message = JSON.stringify(data);
|
const message = JSON.stringify(data);
|
||||||
clients.forEach((info, ws) => {
|
clients.forEach((info, ws) => {
|
||||||
@@ -237,7 +250,6 @@ function broadcastToRoomExcludeSelf(roomId, excludeUsername, data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 广播给房间内所有人
|
|
||||||
function broadcastToRoom(roomId, data) {
|
function broadcastToRoom(roomId, data) {
|
||||||
const message = JSON.stringify(data);
|
const message = JSON.stringify(data);
|
||||||
clients.forEach((info, ws) => {
|
clients.forEach((info, ws) => {
|
||||||
@@ -247,7 +259,6 @@ function broadcastToRoom(roomId, data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全局广播
|
|
||||||
function broadcast(data) {
|
function broadcast(data) {
|
||||||
const message = JSON.stringify(data);
|
const message = JSON.stringify(data);
|
||||||
clients.forEach((ws) => {
|
clients.forEach((ws) => {
|
||||||
@@ -255,7 +266,7 @@ function broadcast(data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内部消息存储并广播(用于 AI 回复等)
|
// 内部消息存储并广播(用于 AI 回复)
|
||||||
function storeAndBroadcastText(roomId, user, text) {
|
function storeAndBroadcastText(roomId, user, text) {
|
||||||
const timestamp = new Date().toLocaleString('zh-CN', { hour12: false });
|
const timestamp = new Date().toLocaleString('zh-CN', { hour12: false });
|
||||||
const result = db.prepare('INSERT INTO messages (room_id, user, text, timestamp) VALUES (?,?,?,?)').run(roomId, user, text, timestamp);
|
const result = db.prepare('INSERT INTO messages (room_id, user, text, timestamp) VALUES (?,?,?,?)').run(roomId, user, text, timestamp);
|
||||||
@@ -285,7 +296,7 @@ function addToHistory(roomId, role, content) {
|
|||||||
if (history.length > 60) conversationHistory.set(roomId, history.slice(-40));
|
if (history.length > 60) conversationHistory.set(roomId, history.slice(-40));
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI 处理(保持原逻辑)
|
// AI 分析
|
||||||
async function analyzeWithDeepSeek(text, historyMessages, username) {
|
async function analyzeWithDeepSeek(text, historyMessages, username) {
|
||||||
const systemPrompt = `你是智能财务助手“小财”。结合对话历史理解用户意图。只返回JSON。
|
const systemPrompt = `你是智能财务助手“小财”。结合对话历史理解用户意图。只返回JSON。
|
||||||
|
|
||||||
@@ -308,13 +319,16 @@ async function analyzeWithDeepSeek(text, historyMessages, username) {
|
|||||||
{ role: 'user', content: `${username}: ${text}` }
|
{ role: 'user', content: `${username}: ${text}` }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
console.log('🤖 调用 DeepSeek...');
|
||||||
const res = await fetch('https://api.deepseek.com/chat/completions', {
|
const res = await fetch('https://api.deepseek.com/chat/completions', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${DEEPSEEK_API_KEY}` },
|
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${DEEPSEEK_API_KEY}` },
|
||||||
body: JSON.stringify({ model: 'deepseek-v4-flash', messages, temperature: 0.1, stream: false })
|
body: JSON.stringify({ model: 'deepseek-v4-flash', messages, temperature: 0.1, stream: false })
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
return JSON.parse(data.choices[0].message.content.replace(/```json|```/g, '').trim());
|
const content = data.choices[0].message.content;
|
||||||
|
console.log('🤖 AI 返回:', content);
|
||||||
|
return JSON.parse(content.replace(/```json|```/g, '').trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAIResult(aiResult, roomId, username, originalText, attachments) {
|
function handleAIResult(aiResult, roomId, username, originalText, attachments) {
|
||||||
|
|||||||
Reference in New Issue
Block a user