const express = require('express'); const cors = require('cors'); const { body, param, query, validationResult } = require('express-validator'); require('dotenv').config(); const db = require('./db'); const app = express(); const PORT = process.env.PORT || 3002; // 中间件 app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 验证错误处理中间件 const validate = (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ success: false, errors: errors.array() }); } next(); }; // 健康检查端点 app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), service: 'Customer Management API' }); }); // ==================== 客户管理 API ==================== // 1. GET /api/customers - 获取客户列表(分页、搜索) app.get('/api/customers', [ query('page').optional().isInt({ min: 1 }).toInt(), query('limit').optional().isInt({ min: 1, max: 100 }).toInt(), query('search').optional().trim(), query('status').optional().trim() ], validate, async (req, res) => { try { const page = req.query.page || 1; const limit = req.query.limit || 10; const offset = (page - 1) * limit; const search = req.query.search || ''; const status = req.query.status || ''; let query = 'SELECT * FROM customers WHERE 1=1'; let queryParams = []; let paramCount = 1; if (search) { query += ` AND (name ILIKE $${paramCount} OR email ILIKE $${paramCount} OR company ILIKE $${paramCount})`; queryParams.push(`%${search}%`); paramCount++; } if (status) { query += ` AND status = $${paramCount}`; queryParams.push(status); paramCount++; } // 获取总数 const countQuery = query.replace('SELECT *', 'SELECT COUNT(*) as total'); const countResult = await db.query(countQuery, queryParams); const total = parseInt(countResult.rows[0].total); // 获取分页数据 query += ` ORDER BY created_at DESC LIMIT $${paramCount} OFFSET $${paramCount + 1}`; queryParams.push(limit, offset); const result = await db.query(query, queryParams); res.json({ success: true, data: result.rows, pagination: { page: parseInt(page), limit: parseInt(limit), total, totalPages: Math.ceil(total / limit) } }); } catch (error) { console.error('Error fetching customers:', error); res.status(500).json({ success: false, message: 'Failed to fetch customers', error: error.message }); } } ); // 2. GET /api/customers/:id - 获取单个客户 app.get('/api/customers/:id', [ param('id').isInt({ min: 1 }) ], validate, async (req, res) => { try { const { id } = req.params; const result = await db.query('SELECT * FROM customers WHERE id = $1', [id]); if (result.rows.length === 0) { return res.status(404).json({ success: false, message: 'Customer not found' }); } res.json({ success: true, data: result.rows[0] }); } catch (error) { console.error('Error fetching customer:', error); res.status(500).json({ success: false, message: 'Failed to fetch customer', error: error.message }); } } ); // 3. POST /api/customers - 创建客户 app.post('/api/customers', [ body('name').notEmpty().trim().withMessage('Name is required'), body('email').notEmpty().trim().isEmail().withMessage('Valid email is required'), body('phone').optional().trim(), body('address').optional().trim(), body('company').optional().trim(), body('tax_id').optional().trim(), body('status').optional().isIn(['active', 'inactive']).withMessage('Status must be active or inactive') ], validate, async (req, res) => { try { const { name, email, phone, address, company, tax_id, status = 'active' } = req.body; const result = await db.query( `INSERT INTO customers (name, email, phone, address, company, tax_id, status) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *`, [name, email, phone, address, company, tax_id, status] ); res.status(201).json({ success: true, message: 'Customer created successfully', data: result.rows[0] }); } catch (error) { console.error('Error creating customer:', error); // 处理唯一约束错误 if (error.code === '23505') { // unique_violation return res.status(409).json({ success: false, message: 'Email already exists' }); } res.status(500).json({ success: false, message: 'Failed to create customer', error: error.message }); } } ); // 4. PUT /api/customers/:id - 更新客户 app.put('/api/customers/:id', [ param('id').isInt({ min: 1 }), body('name').optional().trim(), body('email').optional().trim().isEmail().withMessage('Valid email is required if provided'), body('phone').optional().trim(), body('address').optional().trim(), body('company').optional().trim(), body('tax_id').optional().trim(), body('status').optional().isIn(['active', 'inactive']).withMessage('Status must be active or inactive') ], validate, async (req, res) => { try { const { id } = req.params; const { name, email, phone, address, company, tax_id, status } = req.body; // 检查客户是否存在 const checkResult = await db.query('SELECT * FROM customers WHERE id = $1', [id]); if (checkResult.rows.length === 0) { return res.status(404).json({ success: false, message: 'Customer not found' }); } // 构建更新字段 const updateFields = []; const values = []; let paramCount = 1; if (name !== undefined) { updateFields.push(`name = $${paramCount}`); values.push(name); paramCount++; } if (email !== undefined) { updateFields.push(`email = $${paramCount}`); values.push(email); paramCount++; } if (phone !== undefined) { updateFields.push(`phone = $${paramCount}`); values.push(phone); paramCount++; } if (address !== undefined) { updateFields.push(`address = $${paramCount}`); values.push(address); paramCount++; } if (company !== undefined) { updateFields.push(`company = $${paramCount}`); values.push(company); paramCount++; } if (tax_id !== undefined) { updateFields.push(`tax_id = $${paramCount}`); values.push(tax_id); paramCount++; } if (status !== undefined) { updateFields.push(`status = $${paramCount}`); values.push(status); paramCount++; } // 添加更新时间 updateFields.push(`updated_at = CURRENT_TIMESTAMP`); if (updateFields.length === 1) { // 只有updated_at被更新 return res.status(400).json({ success: false, message: 'No fields to update' }); } values.push(id); const query = `UPDATE customers SET ${updateFields.join(', ')} WHERE id = $${paramCount} RETURNING *`; const result = await db.query(query, values); res.json({ success: true, message: 'Customer updated successfully', data: result.rows[0] }); } catch (error) { console.error('Error updating customer:', error); // 处理唯一约束错误 if (error.code === '23505') { // unique_violation return res.status(409).json({ success: false, message: 'Email already exists' }); } res.status(500).json({ success: false, message: 'Failed to update customer', error: error.message }); } } ); // 5. DELETE /api/customers/:id - 删除客户 app.delete('/api/customers/:id', [ param('id').isInt({ min: 1 }) ], validate, async (req, res) => { try { const { id } = req.params; // 检查客户是否存在 const checkResult = await db.query('SELECT * FROM customers WHERE id = $1', [id]); if (checkResult.rows.length === 0) { return res.status(404).json({ success: false, message: 'Customer not found' }); } await db.query('DELETE FROM customers WHERE id = $1', [id]); res.json({ success: true, message: 'Customer deleted successfully' }); } catch (error) { console.error('Error deleting customer:', error); res.status(500).json({ success: false, message: 'Failed to delete customer', error: error.message }); } } ); // 6. GET /api/customers/:id/contacts - 获取客户联系人 app.get('/api/customers/:id/contacts', [ param('id').isInt({ min: 1 }) ], validate, async (req, res) => { try { const { id } = req.params; // 检查客户是否存在 const customerResult = await db.query('SELECT * FROM customers WHERE id = $1', [id]); if (customerResult.rows.length === 0) { return res.status(404).json({ success: false, message: 'Customer not found' }); } const result = await db.query( 'SELECT * FROM contacts WHERE customer_id = $1 ORDER BY is_primary DESC, created_at DESC', [id] ); res.json({ success: true, data: result.rows }); } catch (error) { console.error('Error fetching customer contacts:', error); res.status(500).json({ success: false, message: 'Failed to fetch customer contacts', error: error.message }); } } ); // 错误处理中间件 app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ success: false, message: 'Internal server error', error: process.env.NODE_ENV === 'development' ? err.message : undefined }); }); // 404处理 app.use((req, res) => { res.status(404).json({ success: false, message: 'Endpoint not found' }); }); // 启动服务器 app.listen(PORT, () => { console.log(`Customer Management API server running on port ${PORT}`); console.log('Available endpoints:'); console.log(' GET /health'); console.log(' GET /api/customers'); console.log(' GET /api/customers/:id'); console.log(' POST /api/customers'); console.log(' PUT /api/customers/:id'); console.log(' DELETE /api/customers/:id'); console.log(' GET /api/customers/:id/contacts'); });