Initial commit: ERP system with advance verification fixes

This commit is contained in:
System Administrator
2026-03-25 23:55:36 +07:00
commit 563ca12d76
5920 changed files with 828689 additions and 0 deletions
@@ -0,0 +1,403 @@
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 || 3000;
// 中间件
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');
});