Files
ai-xiaocai/server/routes/messages.js
2026-07-29 08:00:51 +08:00

82 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 消息路由 + AI 分析
const express = require('express');
const { authMiddleware } = require('../middleware/auth');
const { broadcastToRoomExcludeSelf } = require('../ws');
const { buildSystemPrompt } = require('../ai/prompt');
const { validate, normalize } = require('../ai/validator');
const { callDeepSeek } = require('../ai/client');
const { getHistory, addToHistory, hasPendingAction, handleAIResult, executePendingAction } = require('../handlers');
const db = require('../db');
const { timestamp } = require('../utils');
const router = express.Router();
router.get('/:roomId/messages', authMiddleware, (req, res) => {
const msgs = db.prepare('SELECT * FROM messages WHERE room_id = ? ORDER BY id ASC').all(req.params.roomId);
res.json(msgs);
});
router.post('/:roomId/messages', authMiddleware, async (req, res) => {
const { text, attachments } = req.body;
console.log('📨 收到消息, text:', (text||'').substring(0,30), 'attachments:', JSON.stringify(attachments));
const roomId = req.params.roomId;
const username = req.user.username;
const now = timestamp();
const result = db.prepare('INSERT INTO messages (room_id, user, text, attachments, timestamp) VALUES (?,?,?,?,?)').run(
roomId, username, text || '', attachments ? JSON.stringify(attachments) : null, now
);
const msg = { id: result.lastInsertRowid, room_id: roomId, user: username, text: text || '', attachments: attachments || [], timestamp: now };
const lastMsg = db.prepare('SELECT text, timestamp FROM messages WHERE room_id = ? ORDER BY id DESC LIMIT 1').get(roomId);
broadcastToRoomExcludeSelf(roomId, username, {
type: 'new_message', message: msg,
room_preview: { room_id: roomId, last_message: lastMsg ? lastMsg.text : '', last_time: lastMsg ? lastMsg.timestamp : '' }
});
addToHistory(roomId, 'user', `${username}: ${text || '图片'}`);
res.json(msg);
if (text && text.trim() === '确认' && hasPendingAction(roomId, username)) {
executePendingAction(roomId, username);
return;
}
try {
const history = getHistory(roomId);
const historyMessages = history.map(e => ({ role: e.role, content: e.content }));
const aiResponse = await analyzeWithDeepSeek(text || '', historyMessages, username, req.user.isAdmin, roomId);
if (aiResponse && aiResponse.action !== 'ignore') {
handleAIResult(aiResponse, roomId, username, text || '', attachments || []);
}
} catch (e) { console.error('AI 分析失败:', e); }
});
async function analyzeWithDeepSeek(text, historyMessages, username, isAdmin, roomId) {
// 注入当前房间采购清单上下文
const purchases = db.prepare("SELECT item, quantity, amount, status, created_at FROM purchases WHERE room_id = ? ORDER BY created_at DESC LIMIT 10").all(roomId);
const purchaseContext = purchases.length
? `\n📋 当前房间采购清单最近10条\n${purchases.map(p => `- ${p.item} | ×${p.quantity} | ¥${p.amount} | ${p.status} | ${p.created_at}`).join('\n')}`
: '';
const systemPrompt = buildSystemPrompt(username, isAdmin) + purchaseContext;
const messages = [
{ role: 'system', content: systemPrompt },
...historyMessages.slice(-30),
{ role: 'user', content: `${username}: ${text}` }
];
const content = await callDeepSeek(messages, 0.1, username);
console.log('🤖 AI 返回:', content);
try {
const parsed = JSON.parse(content.replace(/```json|```/g, '').trim());
normalize(parsed);
const v = validate(parsed);
if (!v.valid) console.warn('⚠️ AI 返回校验失败:', v.error);
return parsed;
} catch (e) {
// AI 返回非 JSON如纯文本回复图片消息→ 降级为 chat
console.warn('⚠️ AI 返回非 JSON降级为 chat:', content.substring(0, 50));
return { action: 'chat', reply: content };
}
}
module.exports = router;