const express = require('express'); const { Pool } = require('pg'); const cors = require('cors'); const bodyParser = require('body-parser'); const { v4: uuidv4 } = require('uuid'); const os = require('os'); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const app = express(); const PORT = process.env.PORT || 3000; // 中间件 app.use(cors()); app.use(bodyParser.json({ limit: '50mb' })); app.use(express.static('public')); app.use('/uploads', express.static('uploads')); // 配置文件上传 const storage = multer.diskStorage({ destination: (req, file, cb) => { const dir = 'uploads/'; if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } cb(null, dir); }, filename: (req, file, cb) => { const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`; cb(null, uniqueName); } }); const upload = multer({ storage, limits: { fileSize: 10 * 1024 * 1024 } // 10MB }); // PostgreSQL 连接池 const pool = new Pool({ host: process.env.DB_HOST || 'postgres', port: process.env.DB_PORT || 5432, database: process.env.DB_NAME || 'agent_tasks', user: process.env.DB_USER || 'postgres', password: process.env.DB_PASSWORD || 'postgres', max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); // 测试数据库连接 pool.connect((err, client, release) => { if (err) { console.error('❌ 数据库连接失败:', err.message); process.exit(1); } else { console.log('✅ 已连接到 PostgreSQL 数据库'); release(); initDatabase(); } }); // 创建表结构 async function initDatabase() { const client = await pool.connect(); try { await client.query('BEGIN'); // 智能体表 await client.query(` CREATE TABLE IF NOT EXISTS agents ( id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) NOT NULL, type VARCHAR(100), capabilities JSONB, status VARCHAR(50) DEFAULT 'idle', registered_at BIGINT, last_heartbeat BIGINT ) `); // 项目表 await client.query(` CREATE TABLE IF NOT EXISTS projects ( id VARCHAR(255) PRIMARY KEY, name VARCHAR(500) NOT NULL, description TEXT, created_by VARCHAR(255), created_at BIGINT, updated_at BIGINT, status VARCHAR(50) DEFAULT 'active' ) `); // 协作规约表 await client.query(` CREATE TABLE IF NOT EXISTS collaboration_rules ( id VARCHAR(255) PRIMARY KEY, project_id VARCHAR(255) REFERENCES projects(id) ON DELETE CASCADE, content TEXT NOT NULL, created_by VARCHAR(255), created_at BIGINT, updated_at BIGINT ) `); // 项目信息共享清单表 await client.query(` CREATE TABLE IF NOT EXISTS project_notes ( id VARCHAR(255) PRIMARY KEY, project_id VARCHAR(255) REFERENCES projects(id) ON DELETE CASCADE, content TEXT NOT NULL, created_by VARCHAR(255), created_at BIGINT, updated_at BIGINT ) `); // 任务表(扩展) await client.query(` CREATE TABLE IF NOT EXISTS tasks ( id VARCHAR(255) PRIMARY KEY, project_id VARCHAR(255) REFERENCES projects(id) ON DELETE CASCADE, parent_task_id VARCHAR(255) REFERENCES tasks(id) ON DELETE SET NULL, title VARCHAR(500) NOT NULL, description TEXT, task_type VARCHAR(50) DEFAULT 'development', priority VARCHAR(50) DEFAULT 'normal', status VARCHAR(50) DEFAULT 'initial', created_by VARCHAR(255), assigned_to VARCHAR(255), created_at BIGINT, updated_at BIGINT, completed_at BIGINT, result TEXT ) `); // 附件表 await client.query(` CREATE TABLE IF NOT EXISTS attachments ( id VARCHAR(255) PRIMARY KEY, task_id VARCHAR(255) REFERENCES tasks(id) ON DELETE CASCADE, filename VARCHAR(500) NOT NULL, original_name VARCHAR(500) NOT NULL, file_path VARCHAR(1000) NOT NULL, file_size BIGINT, mime_type VARCHAR(100), uploaded_by VARCHAR(255), uploaded_at BIGINT ) `); // 创建索引 await client.query(`CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status)`); await client.query(`CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)`); await client.query(`CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id)`); await client.query(`CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id)`); await client.query(`CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to)`); await client.query(`CREATE INDEX IF NOT EXISTS idx_attachments_task ON attachments(task_id)`); await client.query(`CREATE INDEX IF NOT EXISTS idx_project_notes_project ON project_notes(project_id)`); await client.query('COMMIT'); console.log('✅ 数据库表已初始化'); } catch (err) { await client.query('ROLLBACK'); console.error('❌ 数据库初始化失败:', err); } finally { client.release(); } } // ==================== 项目管理 API ==================== // 创建项目 app.post('/api/projects', async (req, res) => { const { name, description, created_by } = req.body; if (!name) { return res.status(400).json({ error: '项目名称不能为空' }); } const id = uuidv4(); const now = Date.now(); try { await pool.query( `INSERT INTO projects (id, name, description, created_by, created_at, updated_at, status) VALUES ($1, $2, $3, $4, $5, $6, 'active')`, [id, name, description || '', created_by || 'human', now, now] ); res.json({ id, name, status: 'active', message: '项目创建成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取所有项目 app.get('/api/projects', async (req, res) => { try { const result = await pool.query('SELECT * FROM projects ORDER BY created_at DESC'); res.json(result.rows); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取单个项目详情 app.get('/api/projects/:id', async (req, res) => { try { const result = await pool.query('SELECT * FROM projects WHERE id = $1', [req.params.id]); if (result.rows.length === 0) { return res.status(404).json({ error: '项目不存在' }); } res.json(result.rows[0]); } catch (err) { res.status(500).json({ error: err.message }); } }); // 更新项目 app.put('/api/projects/:id', async (req, res) => { const { id } = req.params; const { name, description, status } = req.body; const updates = []; const params = []; let paramIndex = 1; if (name) { updates.push(`name = $${paramIndex++}`); params.push(name); } if (description !== undefined) { updates.push(`description = $${paramIndex++}`); params.push(description); } if (status) { updates.push(`status = $${paramIndex++}`); params.push(status); } updates.push(`updated_at = $${paramIndex++}`); params.push(Date.now()); params.push(id); try { const result = await pool.query( `UPDATE projects SET ${updates.join(', ')} WHERE id = $${paramIndex}`, params ); if (result.rowCount === 0) { return res.status(404).json({ error: '项目不存在' }); } res.json({ message: '项目更新成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ==================== 协作规约 API ==================== // 设置协作规约 app.post('/api/projects/:projectId/rules', async (req, res) => { const { projectId } = req.params; const { content, created_by } = req.body; if (!content) { return res.status(400).json({ error: '规约内容不能为空' }); } const id = uuidv4(); const now = Date.now(); try { // 检查项目是否存在 const projectCheck = await pool.query('SELECT id FROM projects WHERE id = $1', [projectId]); if (projectCheck.rows.length === 0) { return res.status(404).json({ error: '项目不存在' }); } // 删除旧规约(每个项目只保留一个) await pool.query('DELETE FROM collaboration_rules WHERE project_id = $1', [projectId]); // 创建新规约 await pool.query( `INSERT INTO collaboration_rules (id, project_id, content, created_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)`, [id, projectId, content, created_by || 'human', now, now] ); res.json({ id, message: '协作规约设置成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取协作规约 app.get('/api/projects/:projectId/rules', async (req, res) => { try { const result = await pool.query( 'SELECT * FROM collaboration_rules WHERE project_id = $1', [req.params.projectId] ); res.json(result.rows[0] || null); } catch (err) { res.status(500).json({ error: err.message }); } }); // ==================== 信息共享清单 API ==================== // 添加共享笔记 app.post('/api/projects/:projectId/notes', async (req, res) => { const { projectId } = req.params; const { content, created_by } = req.body; if (!content) { return res.status(400).json({ error: '笔记内容不能为空' }); } const id = uuidv4(); const now = Date.now(); try { await pool.query( `INSERT INTO project_notes (id, project_id, content, created_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)`, [id, projectId, content, created_by || 'human', now, now] ); res.json({ id, message: '笔记添加成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取共享笔记列表 app.get('/api/projects/:projectId/notes', async (req, res) => { try { const result = await pool.query( 'SELECT * FROM project_notes WHERE project_id = $1 ORDER BY created_at DESC', [req.params.projectId] ); res.json(result.rows); } catch (err) { res.status(500).json({ error: err.message }); } }); // 更新共享笔记 app.put('/api/projects/:projectId/notes/:noteId', async (req, res) => { const { noteId } = req.params; const { content } = req.body; try { const result = await pool.query( 'UPDATE project_notes SET content = $1, updated_at = $2 WHERE id = $3', [content, Date.now(), noteId] ); if (result.rowCount === 0) { return res.status(404).json({ error: '笔记不存在' }); } res.json({ message: '笔记更新成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 删除共享笔记 app.delete('/api/projects/:projectId/notes/:noteId', async (req, res) => { try { const result = await pool.query( 'DELETE FROM project_notes WHERE id = $1', [req.params.noteId] ); if (result.rowCount === 0) { return res.status(404).json({ error: '笔记不存在' }); } res.json({ message: '笔记删除成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ==================== 智能体 API ==================== // 注册智能体 app.post('/api/agents/register', async (req, res) => { const { name, type, capabilities } = req.body; if (!name) { return res.status(400).json({ error: '智能体名称不能为空' }); } const id = uuidv4(); const now = Date.now(); try { await pool.query( `INSERT INTO agents (id, name, type, capabilities, status, registered_at, last_heartbeat) VALUES ($1, $2, $3, $4, 'idle', $5, $6)`, [id, name, type || 'general', JSON.stringify(capabilities || []), now, now] ); res.json({ id, name, type: type || 'general', status: 'idle', message: '智能体注册成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取所有智能体 app.get('/api/agents', async (req, res) => { try { const result = await pool.query('SELECT * FROM agents ORDER BY registered_at DESC'); res.json(result.rows.map(row => ({ ...row, capabilities: row.capabilities || [] }))); } catch (err) { res.status(500).json({ error: err.message }); } }); // 更新智能体状态(心跳) app.post('/api/agents/:id/heartbeat', async (req, res) => { const { id } = req.params; const { status } = req.body; try { const result = await pool.query( 'UPDATE agents SET status = $1, last_heartbeat = $2 WHERE id = $3', [status || 'idle', Date.now(), id] ); if (result.rowCount === 0) { return res.status(404).json({ error: '智能体不存在' }); } res.json({ message: '心跳更新成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ==================== 任务管理 API ==================== // 创建任务(支持子任务) app.post('/api/tasks', async (req, res) => { const { project_id, parent_task_id, title, description, task_type, priority, created_by } = req.body; if (!title) { return res.status(400).json({ error: '任务标题不能为空' }); } if (!project_id) { return res.status(400).json({ error: '必须指定项目ID' }); } const id = uuidv4(); const now = Date.now(); try { await pool.query( `INSERT INTO tasks (id, project_id, parent_task_id, title, description, task_type, priority, status, created_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, 'initial', $8, $9, $10)`, [id, project_id, parent_task_id || null, title, description || '', task_type || 'development', priority || 'normal', created_by || 'human', now, now] ); res.json({ id, title, status: 'initial', message: '任务创建成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取任务列表(支持按项目、状态、父任务过滤) app.get('/api/tasks', async (req, res) => { const { project_id, status, parent_task_id } = req.query; try { let query = 'SELECT * FROM tasks WHERE 1=1'; const params = []; let paramIndex = 1; if (project_id) { query += ` AND project_id = $${paramIndex++}`; params.push(project_id); } if (status) { query += ` AND status = $${paramIndex++}`; params.push(status); } if (parent_task_id) { query += ` AND parent_task_id = $${paramIndex++}`; params.push(parent_task_id); } else if (req.query.root === 'true') { query += ' AND parent_task_id IS NULL'; } query += ' ORDER BY created_at DESC'; const result = await pool.query(query, params); res.json(result.rows); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取任务详情(包含子任务和附件) app.get('/api/tasks/:id', async (req, res) => { try { const taskResult = await pool.query('SELECT * FROM tasks WHERE id = $1', [req.params.id]); if (taskResult.rows.length === 0) { return res.status(404).json({ error: '任务不存在' }); } const task = taskResult.rows[0]; // 获取子任务 const subtasksResult = await pool.query( 'SELECT * FROM tasks WHERE parent_task_id = $1 ORDER BY created_at', [req.params.id] ); // 获取附件 const attachmentsResult = await pool.query( 'SELECT * FROM attachments WHERE task_id = $1 ORDER BY uploaded_at', [req.params.id] ); res.json({ ...task, subtasks: subtasksResult.rows, attachments: attachmentsResult.rows }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 领取任务 app.post('/api/tasks/:id/claim', async (req, res) => { const { id } = req.params; const { agent_id } = req.body; if (!agent_id) { return res.status(400).json({ error: '需要提供智能体ID' }); } const client = await pool.connect(); try { await client.query('BEGIN'); const taskResult = await client.query('SELECT status, project_id FROM tasks WHERE id = $1', [id]); if (taskResult.rows.length === 0) { await client.query('ROLLBACK'); return res.status(404).json({ error: '任务不存在' }); } const task = taskResult.rows[0]; if (task.status !== 'initial' && task.status !== 'in_progress') { await client.query('ROLLBACK'); return res.status(400).json({ error: '任务状态不允许领取' }); } // 获取项目的协作规约 const rulesResult = await client.query( 'SELECT content FROM collaboration_rules WHERE project_id = $1', [task.project_id] ); await client.query( 'UPDATE tasks SET status = $1, assigned_to = $2, updated_at = $3 WHERE id = $4', ['in_progress', agent_id, Date.now(), id] ); await client.query('UPDATE agents SET status = $1 WHERE id = $2', ['busy', agent_id]); await client.query('COMMIT'); const response = { message: '任务领取成功' }; // 如果有协作规约,返回给智能体 if (rulesResult.rows.length > 0) { response.collaboration_rules = rulesResult.rows[0].content; response.notice = '⚠️ 请严格遵守项目协作规约'; } res.json(response); } catch (err) { await client.query('ROLLBACK'); res.status(500).json({ error: err.message }); } finally { client.release(); } }); // 更新任务状态 app.put('/api/tasks/:id', async (req, res) => { const { id } = req.params; const { status, result, description, task_type, priority } = req.body; const client = await pool.connect(); try { await client.query('BEGIN'); const now = Date.now(); const updates = []; const params = []; let paramIndex = 1; if (status) { updates.push(`status = $${paramIndex++}`); params.push(status); } if (result !== undefined) { updates.push(`result = $${paramIndex++}`); params.push(result); } if (description !== undefined) { updates.push(`description = $${paramIndex++}`); params.push(description); } if (task_type) { updates.push(`task_type = $${paramIndex++}`); params.push(task_type); } if (priority) { updates.push(`priority = $${paramIndex++}`); params.push(priority); } if (status === 'completed') { updates.push(`completed_at = $${paramIndex++}`); params.push(now); } updates.push(`updated_at = $${paramIndex++}`); params.push(now); params.push(id); const updateResult = await client.query( `UPDATE tasks SET ${updates.join(', ')} WHERE id = $${paramIndex}`, params ); if (updateResult.rowCount === 0) { await client.query('ROLLBACK'); return res.status(404).json({ error: '任务不存在' }); } // 如果任务完成,将智能体状态改为 idle if (status === 'completed') { const taskResult = await client.query('SELECT assigned_to FROM tasks WHERE id = $1', [id]); if (taskResult.rows.length > 0 && taskResult.rows[0].assigned_to) { await client.query('UPDATE agents SET status = $1 WHERE id = $2', ['idle', taskResult.rows[0].assigned_to]); } } await client.query('COMMIT'); res.json({ message: '任务状态更新成功' }); } catch (err) { await client.query('ROLLBACK'); res.status(500).json({ error: err.message }); } finally { client.release(); } }); // ==================== 附件管理 API ==================== // 上传附件 app.post('/api/tasks/:taskId/attachments', upload.single('file'), async (req, res) => { const { taskId } = req.params; const { uploaded_by } = req.body; if (!req.file) { return res.status(400).json({ error: '没有上传文件' }); } const id = uuidv4(); const now = Date.now(); try { await pool.query( `INSERT INTO attachments (id, task_id, filename, original_name, file_path, file_size, mime_type, uploaded_by, uploaded_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [id, taskId, req.file.filename, req.file.originalname, req.file.path, req.file.size, req.file.mimetype, uploaded_by || 'human', now] ); res.json({ id, filename: req.file.filename, original_name: req.file.originalname, url: `/uploads/${req.file.filename}`, message: '附件上传成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 获取任务附件列表 app.get('/api/tasks/:taskId/attachments', async (req, res) => { try { const result = await pool.query( 'SELECT * FROM attachments WHERE task_id = $1 ORDER BY uploaded_at DESC', [req.params.taskId] ); res.json(result.rows.map(row => ({ ...row, url: `/uploads/${row.filename}` }))); } catch (err) { res.status(500).json({ error: err.message }); } }); // 删除附件 app.delete('/api/tasks/:taskId/attachments/:attachmentId', async (req, res) => { try { const result = await pool.query( 'SELECT file_path FROM attachments WHERE id = $1', [req.params.attachmentId] ); if (result.rows.length === 0) { return res.status(404).json({ error: '附件不存在' }); } // 删除文件 const filePath = result.rows[0].file_path; if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } // 删除数据库记录 await pool.query('DELETE FROM attachments WHERE id = $1', [req.params.attachmentId]); res.json({ message: '附件删除成功' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ==================== 统计信息 API ==================== app.get('/api/stats', async (req, res) => { try { const stats = { total_agents: 0, active_agents: 0, total_projects: 0, total_tasks: 0, tasks_by_status: {}, tasks_by_type: {} }; const results = await Promise.all([ pool.query('SELECT COUNT(*) as count FROM agents'), pool.query('SELECT COUNT(*) as count FROM agents WHERE status = $1', ['busy']), pool.query('SELECT COUNT(*) as count FROM projects'), pool.query('SELECT COUNT(*) as count FROM tasks'), pool.query('SELECT status, COUNT(*) as count FROM tasks GROUP BY status'), pool.query('SELECT task_type, COUNT(*) as count FROM tasks GROUP BY task_type') ]); stats.total_agents = parseInt(results[0].rows[0].count); stats.active_agents = parseInt(results[1].rows[0].count); stats.total_projects = parseInt(results[2].rows[0].count); stats.total_tasks = parseInt(results[3].rows[0].count); results[4].rows.forEach(row => { stats.tasks_by_status[row.status] = parseInt(row.count); }); results[5].rows.forEach(row => { stats.tasks_by_type[row.task_type] = parseInt(row.count); }); res.json(stats); } catch (err) { res.status(500).json({ error: err.message }); } }); // 健康检查 app.get('/health', async (req, res) => { try { await pool.query('SELECT 1'); res.json({ status: 'healthy', database: 'connected' }); } catch (err) { res.status(503).json({ status: 'unhealthy', error: err.message }); } }); // 获取本机 IP 地址 function getLocalIP() { const interfaces = os.networkInterfaces(); for (const name of Object.keys(interfaces)) { for (const iface of interfaces[name]) { if (iface.family === 'IPv4' && !iface.internal) { return iface.address; } } } return 'localhost'; } // 启动服务器 app.listen(PORT, '0.0.0.0', () => { const localIP = getLocalIP(); console.log(`🚀 多智能体任务服务已启动 (v2.0)`); console.log(` 本地访问: http://localhost:${PORT}`); console.log(` 远程访问: http://${localIP}:${PORT}`); }); // 优雅关闭 process.on('SIGINT', async () => { console.log('\n正在关闭服务...'); await pool.end(); console.log('数据库连接已关闭'); process.exit(0); });