const express = require('express'); const db = require('../db'); const { authenticate, requireAdmin } = require('../middleware/auth'); const router = express.Router(); // 获取施工项目列表 router.get('/my-projects', async (req, res) => { try { const result = await db.query(` SELECT p.*, c.name as customer_name, u.name as manager_name FROM projects p LEFT JOIN customers c ON p.customer_id = c.id LEFT JOIN users u ON p.project_manager_id = u.id WHERE p.status IN ('active', 'in_progress', 'planning', 'pending', 'completed') ORDER BY p.created_at DESC `); const projects = result.rows.map(project => ({ ...project, latest_log: null, progress: 0 })); res.json({ success: true, data: projects, count: projects.length }); } catch (error) { console.error('获取施工项目失败:', error); res.status(500).json({ success: false, message: '操作失败' }); } }); // ==================== 施工日志 ==================== // 获取项目施工日志 router.get('/projects/:projectId/logs', async (req, res) => { try { const { projectId } = req.params; const result = await db.query( 'SELECT * FROM construction_logs WHERE project_id = $1 ORDER BY log_date DESC, created_at DESC', [projectId] ); res.json({ success: true, data: result.rows }); } catch (error) { console.error('获取施工日志失败:', error); res.status(500).json({ success: false, message: '获取施工日志失败' }); } }); // 新增施工日志 router.post('/projects/:projectId/logs', authenticate, async (req, res) => { try { const { projectId } = req.params; const { log_date, weather, work_content, next_plan, issues, photos } = req.body; const userId = req.user?.id || req.user?.userId; const result = await db.query( `INSERT INTO construction_logs (project_id, log_date, weather, recorded_by, work_content, next_plan, issues, photos) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`, [projectId, log_date, weather || 'sunny', userId, work_content || '', next_plan || '', issues || '', photos || []] ); res.json({ success: true, data: result.rows[0] }); } catch (error) { console.error('新增施工日志失败:', error); res.status(500).json({ success: false, message: '新增施工日志失败' }); } }); // 删除施工日志 router.delete('/logs/:logId', authenticate, async (req, res) => { try { const { logId } = req.params; const result = await db.query('DELETE FROM construction_logs WHERE id = $1 RETURNING id', [logId]); if (result.rowCount === 0) { return res.status(404).json({ success: false, message: '日志不存在' }); } res.json({ success: true, message: '删除成功' }); } catch (error) { console.error('删除施工日志失败:', error); res.status(500).json({ success: false, message: '删除施工日志失败' }); } }); // ==================== 施工进度 ==================== // 获取项目施工进度(包含phases和项目信息) router.get('/projects/:projectId/progress', async (req, res) => { try { const { projectId } = req.params; // 获取项目信息 const projectResult = await db.query( `SELECT p.*, c.name as customer_name, u.name as manager_name FROM projects p LEFT JOIN customers c ON p.customer_id = c.id LEFT JOIN users u ON p.project_manager_id = u.id WHERE p.id = $1`, [projectId] ); if (projectResult.rowCount === 0) { return res.status(404).json({ success: false, message: '项目不存在' }); } // 获取阶段 const phasesResult = await db.query( 'SELECT * FROM project_phases WHERE project_id = $1 ORDER BY phase_order', [projectId] ); res.json({ success: true, data: { project: projectResult.rows[0], phases: phasesResult.rows } }); } catch (error) { console.error('获取施工进度失败:', error); res.status(500).json({ success: false, message: '获取施工进度失败' }); } }); // 完成阶段 router.post('/projects/:projectId/phases/:phaseId/complete', authenticate, async (req, res) => { try { const { projectId, phaseId } = req.params; const { remark, photos } = req.body; const userId = req.user?.id || req.user?.userId; // 标记阶段完成 await db.query( `UPDATE project_phases SET status = 'completed', completed_at = CURRENT_TIMESTAMP, completed_by = $1, remark = COALESCE(remark, '') || $2, photos = COALESCE(photos, '{}') || $3 WHERE id = $4 AND project_id = $5`, [userId, remark ? '\n' + remark : '', photos || [], phaseId, projectId] ); // 计算总体进度 const progressResult = await db.query( 'SELECT COUNT(*) as total, COUNT(CASE WHEN status = $1 THEN 1 END) as completed FROM project_phases WHERE project_id = $2', ['completed', projectId] ); const total = parseInt(progressResult.rows[0].total); const completed = parseInt(progressResult.rows[0].completed); const progress = total > 0 ? Math.round((completed / total) * 100) : 0; // 找下一个待开始的阶段 const nextPhaseResult = await db.query( `SELECT * FROM project_phases WHERE project_id = $1 AND status = 'pending' ORDER BY phase_order LIMIT 1`, [projectId] ); if (nextPhaseResult.rows.length > 0) { await db.query( `UPDATE project_phases SET status = 'in_progress', started_at = CURRENT_TIMESTAMP WHERE id = $1`, [nextPhaseResult.rows[0].id] ); // 更新项目当前阶段 await db.query( `UPDATE projects SET current_phase = $1, phase_progress = $2 WHERE id = $3`, [nextPhaseResult.rows[0].phase_name, progress, projectId] ); } else { // 所有阶段完成 await db.query( `UPDATE projects SET current_phase = '已完成', phase_progress = 100, status = 'completed' WHERE id = $1`, [projectId] ); } res.json({ success: true, progress }); } catch (error) { console.error('完成阶段失败:', error); res.status(500).json({ success: false, message: '完成阶段失败' }); } }); // 重新打开阶段 router.put('/phases/:phaseId/reopen', authenticate, async (req, res) => { try { const { phaseId } = req.params; await db.query( `UPDATE project_phases SET status = 'in_progress', completed_at = NULL, completed_by = NULL WHERE id = $1`, [phaseId] ); // 重新计算进度 const phaseResult = await db.query('SELECT project_id FROM project_phases WHERE id = $1', [phaseId]); if (phaseResult.rows.length > 0) { const projectId = phaseResult.rows[0].project_id; const progressResult = await db.query( 'SELECT COUNT(*) as total, COUNT(CASE WHEN status = $1 THEN 1 END) as completed FROM project_phases WHERE project_id = $2', ['completed', projectId] ); const total = parseInt(progressResult.rows[0].total); const completed = parseInt(progressResult.rows[0].completed); const progress = total > 0 ? Math.round((completed / total) * 100) : 0; await db.query( `UPDATE projects SET phase_progress = $1, status = 'active' WHERE id = $2`, [progress, projectId] ); } res.json({ success: true }); } catch (error) { console.error('重新打开阶段失败:', error); res.status(500).json({ success: false, message: '重新打开阶段失败' }); } }); // 更新子项状态(支持完成时间、照片、与资料管理共享) router.put('/phases/:phaseId/sub-item', authenticate, async (req, res) => { try { const { phaseId } = req.params; const { itemIndex, completed, completed_at, photos } = req.body; const userId = req.user?.id || req.user?.userId; const phaseResult = await db.query('SELECT sub_items, project_id FROM project_phases WHERE id = $1', [phaseId]); if (phaseResult.rowCount === 0) { return res.status(404).json({ success: false, message: '阶段不存在' }); } const subItems = phaseResult.rows[0].sub_items || []; const projectId = phaseResult.rows[0].project_id; if (itemIndex >= 0 && itemIndex < subItems.length) { subItems[itemIndex].completed = completed; if (completed) { subItems[itemIndex].completed_at = completed_at || new Date().toISOString(); if (photos && photos.length > 0) { subItems[itemIndex].photos = photos; // 同步写入 project_documents 实现与资料管理共享 for (const url of photos) { const fileName = url.split('/').pop() || 'photo.jpg'; await db.query( `INSERT INTO project_documents (project_id, doc_type, file_name, file_url, description, uploaded_by) VALUES ($1, 'image', $2, $3, $4, $5)`, [projectId, fileName, url, `阶段子项: ${subItems[itemIndex].name}`, userId] ); } } } else { subItems[itemIndex].completed_at = null; subItems[itemIndex].photos = []; } await db.query('UPDATE project_phases SET sub_items = $1 WHERE id = $2', [JSON.stringify(subItems), phaseId]); } res.json({ success: true, subItems }); } catch (error) { console.error('更新子项状态失败:', error); res.status(500).json({ success: false, message: '更新子项状态失败' }); } }); // ==================== 资料管理 ==================== // 获取项目资料列表 router.get('/projects/:projectId/documents', async (req, res) => { try { const { projectId } = req.params; const { doc_type } = req.query; let query = 'SELECT d.*, u.name as uploader_name FROM project_documents d LEFT JOIN users u ON d.uploaded_by = u.id WHERE d.project_id = $1'; const params = [projectId]; if (doc_type) { query += ' AND d.doc_type = $2'; params.push(doc_type); } query += ' ORDER BY d.created_at DESC'; const result = await db.query(query, params); res.json({ success: true, data: result.rows }); } catch (error) { console.error('获取项目资料失败:', error); res.status(500).json({ success: false, message: '获取项目资料失败' }); } }); // 上传项目资料 router.post('/projects/:projectId/documents', authenticate, async (req, res) => { try { const { projectId } = req.params; const { doc_type, file_name, file_url, description } = req.body; const userId = req.user?.id || req.user?.userId; const result = await db.query( `INSERT INTO project_documents (project_id, doc_type, file_name, file_url, description, uploaded_by) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`, [projectId, doc_type || 'document', file_name, file_url, description || '', userId] ); res.json({ success: true, data: result.rows[0] }); } catch (error) { console.error('上传项目资料失败:', error); res.status(500).json({ success: false, message: '上传项目资料失败' }); } }); // 删除项目资料 router.delete('/documents/:docId', authenticate, async (req, res) => { try { const { docId } = req.params; const userId = req.user?.id || req.user?.userId; const isAdmin = req.user?.role === 'admin'; // 管理员或上传者可删除 const docResult = await db.query('SELECT * FROM project_documents WHERE id = $1', [docId]); if (docResult.rowCount === 0) { return res.status(404).json({ success: false, message: '资料不存在' }); } if (!isAdmin && docResult.rows[0].uploaded_by !== userId) { return res.status(403).json({ success: false, message: '无权删除' }); } await db.query('DELETE FROM project_documents WHERE id = $1', [docId]); res.json({ success: true, message: '删除成功' }); } catch (error) { console.error('删除项目资料失败:', error); res.status(500).json({ success: false, message: '删除项目资料失败' }); } }); module.exports = router;