|
|
@@ -1,6 +1,6 @@
|
|
|
const express = require('express');
|
|
|
const router = express.Router();
|
|
|
-const pool = require('../db');
|
|
|
+const { getPool } = require('../db');
|
|
|
const ExcelJS = require('exceljs');
|
|
|
const multer = require('multer');
|
|
|
const fs = require('fs');
|
|
|
@@ -27,14 +27,19 @@ const USERS = [
|
|
|
{ username: 'user', password: 'user123', role: 'user' },
|
|
|
];
|
|
|
|
|
|
+// 根据请求头选择数据库中间件
|
|
|
+function dbPool(req, res, next) {
|
|
|
+ req.db = getPool(req.headers['x-project']);
|
|
|
+ next();
|
|
|
+}
|
|
|
+
|
|
|
// 登录接口
|
|
|
router.post('/login', async (req, res) => {
|
|
|
const { username, password } = req.body;
|
|
|
const user = USERS.find(u => u.username === username && u.password === password);
|
|
|
if (user) {
|
|
|
const token = Buffer.from(`${username}:${user.role}:${Date.now()}`).toString('base64');
|
|
|
- // 记录登录日志
|
|
|
- await logOperation(user.username, user.role, '用户登录', `${user.username} 登录系统`, req.ip);
|
|
|
+ await logOperation(getPool('js'), user.username, user.role, '用户登录', `${user.username} 登录系统`, req.ip);
|
|
|
res.json({ success: true, token, username: user.username, role: user.role });
|
|
|
} else {
|
|
|
res.json({ success: false, message: '用户名或密码错误' });
|
|
|
@@ -67,17 +72,15 @@ function adminOnly(req, res, next) {
|
|
|
}
|
|
|
|
|
|
// 记录操作日志(数据库 + 本地文件)
|
|
|
-async function logOperation(username, role, action, detail, ip) {
|
|
|
- // 写入数据库
|
|
|
+async function logOperation(db, username, role, action, detail, ip) {
|
|
|
try {
|
|
|
- await pool.query(
|
|
|
+ await db.query(
|
|
|
`INSERT INTO sys_operation_log (username, role, action, detail, ip) VALUES ($1, $2, $3, $4, $5)`,
|
|
|
[username, role, action, detail, ip]
|
|
|
);
|
|
|
} catch (e) {
|
|
|
console.error('记录数据库日志失败:', e.message);
|
|
|
}
|
|
|
- // 写入本地文件
|
|
|
try {
|
|
|
const now = new Date();
|
|
|
const dateStr = now.toISOString().replace('T', ' ').substring(0, 19);
|
|
|
@@ -98,7 +101,7 @@ const STATUS_MAP = {
|
|
|
};
|
|
|
|
|
|
// 动态查询订单
|
|
|
-router.post('/query', authMiddleware, async (req, res) => {
|
|
|
+router.post('/query', authMiddleware, dbPool, async (req, res) => {
|
|
|
try {
|
|
|
const { receiver_name, receiver_phone, order_no, start_date, end_date, status, page = 1, pageSize = 20 } = req.body;
|
|
|
|
|
|
@@ -174,7 +177,7 @@ router.post('/query', authMiddleware, async (req, res) => {
|
|
|
}
|
|
|
|
|
|
const countSql = sql.replace(/SELECT[\s\S]*?FROM/, 'SELECT COUNT(*) AS total FROM');
|
|
|
- const countResult = await pool.query(countSql, params);
|
|
|
+ const countResult = await req.db.query(countSql, params);
|
|
|
const total = parseInt(countResult.rows[0].total);
|
|
|
|
|
|
const pageNum = Math.max(1, parseInt(page));
|
|
|
@@ -183,8 +186,8 @@ router.post('/query', authMiddleware, async (req, res) => {
|
|
|
sql += ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
|
params.push(pageSizeNum, (pageNum - 1) * pageSizeNum);
|
|
|
|
|
|
- const result = await pool.query(sql, params);
|
|
|
- await logOperation(req.user.username, req.user.role, '查询订单', `条件: 状态=${status||'全部'}, 收货人=${receiver_name||'-'}, 手机号=${receiver_phone||'-'}, 订单号=${order_no||'-'}, 日期=${start_date||'-'}~${end_date||'-'}, 第${pageNum}页`, req.ip);
|
|
|
+ const result = await req.db.query(sql, params);
|
|
|
+ await logOperation(req.db, req.user.username, req.user.role, '查询订单', `条件: 状态=${status||'全部'}, 收货人=${receiver_name||'-'}, 手机号=${receiver_phone||'-'}, 订单号=${order_no||'-'}, 日期=${start_date||'-'}~${end_date||'-'}, 第${pageNum}页`, req.ip);
|
|
|
res.json({ success: true, data: result.rows, total, page: pageNum, pageSize: pageSizeNum });
|
|
|
} catch (err) {
|
|
|
console.error('查询订单失败:', err);
|
|
|
@@ -193,7 +196,7 @@ router.post('/query', authMiddleware, async (req, res) => {
|
|
|
});
|
|
|
|
|
|
// 导出 Excel
|
|
|
-router.post('/export/excel', authMiddleware, async (req, res) => {
|
|
|
+router.post('/export/excel', authMiddleware, dbPool, async (req, res) => {
|
|
|
try {
|
|
|
const { batch_no, card_no, order_no, start_date, end_date, status } = req.body;
|
|
|
|
|
|
@@ -265,7 +268,7 @@ router.post('/export/excel', authMiddleware, async (req, res) => {
|
|
|
|
|
|
sql += ' ORDER BY c.batch_no, o.create_time, o.order_no';
|
|
|
|
|
|
- const result = await pool.query(sql, params);
|
|
|
+ const result = await req.db.query(sql, params);
|
|
|
|
|
|
const workbook = new ExcelJS.Workbook();
|
|
|
const sheet = workbook.addWorksheet('订单列表');
|
|
|
@@ -322,8 +325,7 @@ router.post('/export/excel', authMiddleware, async (req, res) => {
|
|
|
|
|
|
await workbook.xlsx.write(res);
|
|
|
res.end();
|
|
|
- // 记录导出日志
|
|
|
- await logOperation(req.user.username, req.user.role, '导出Excel', `导出${result.rows.length}条订单`, req.ip);
|
|
|
+ await logOperation(req.db, req.user.username, req.user.role, '导出Excel', `导出${result.rows.length}条订单`, req.ip);
|
|
|
} catch (err) {
|
|
|
console.error('导出Excel失败:', err);
|
|
|
res.status(500).json({ success: false, message: err.message });
|
|
|
@@ -331,7 +333,7 @@ router.post('/export/excel', authMiddleware, async (req, res) => {
|
|
|
});
|
|
|
|
|
|
// 导入运单信息 - 批量更新物流(不允许重复发货)
|
|
|
-router.post('/import/logistics', authMiddleware, upload.single('file'), async (req, res) => {
|
|
|
+router.post('/import/logistics', authMiddleware, dbPool, upload.single('file'), async (req, res) => {
|
|
|
try {
|
|
|
if (!req.file) {
|
|
|
return res.status(400).json({ success: false, message: '请上传文件' });
|
|
|
@@ -388,7 +390,6 @@ router.post('/import/logistics', authMiddleware, upload.single('file'), async (r
|
|
|
if (NAME_TO_CODE[company]) {
|
|
|
company = NAME_TO_CODE[company];
|
|
|
} else if (!CODE_TO_NAME[company]) {
|
|
|
- // 既不是中文名也不是代码,视为无效
|
|
|
results.failed++;
|
|
|
results.errors.push(`第${i + 2}行: 物流公司 "${company}" 无效,请从下拉列表中选择`);
|
|
|
continue;
|
|
|
@@ -407,16 +408,16 @@ router.post('/import/logistics', authMiddleware, upload.single('file'), async (r
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- await pool.query('BEGIN');
|
|
|
+ await req.db.query('BEGIN');
|
|
|
|
|
|
// 查询订单是否存在及状态
|
|
|
- const orderResult = await pool.query(
|
|
|
+ const orderResult = await req.db.query(
|
|
|
`SELECT id, status FROM gift_order WHERE order_no = $1 AND del_flag = '0'`,
|
|
|
[orderNo]
|
|
|
);
|
|
|
|
|
|
if (orderResult.rows.length === 0) {
|
|
|
- await pool.query('ROLLBACK');
|
|
|
+ await req.db.query('ROLLBACK');
|
|
|
results.failed++;
|
|
|
results.errors.push(`第${i + 2}行: 订单 ${orderNo} 不存在`);
|
|
|
continue;
|
|
|
@@ -427,27 +428,27 @@ router.post('/import/logistics', authMiddleware, upload.single('file'), async (r
|
|
|
|
|
|
// 不允许重复发货/已完成
|
|
|
if (orderStatus === 3) {
|
|
|
- await pool.query('ROLLBACK');
|
|
|
+ await req.db.query('ROLLBACK');
|
|
|
results.failed++;
|
|
|
results.errors.push(`第${i + 2}行: 订单 ${orderNo} 已发货,不能重复导入`);
|
|
|
continue;
|
|
|
}
|
|
|
if (orderStatus === 1) {
|
|
|
- await pool.query('ROLLBACK');
|
|
|
+ await req.db.query('ROLLBACK');
|
|
|
results.failed++;
|
|
|
results.errors.push(`第${i + 2}行: 订单 ${orderNo} 已完成,不能导入`);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 更新订单状态为已发货,同时更新备注
|
|
|
- await pool.query(
|
|
|
+ await req.db.query(
|
|
|
`UPDATE gift_order SET status = 3, remark = COALESCE($1, remark), update_time = now(), update_by = $2
|
|
|
WHERE id = $3 AND del_flag = '0'`,
|
|
|
[remark, 'import', orderId]
|
|
|
);
|
|
|
|
|
|
// 插入或更新物流信息
|
|
|
- await pool.query(
|
|
|
+ await req.db.query(
|
|
|
`INSERT INTO gift_logistics (order_id, order_no, company, tracking_no, status, waybill_status, create_time, create_by, update_time, update_by, del_flag)
|
|
|
VALUES ($1, $2, $3, $4, 0, '已发货', now(), 'import', now(), 'import', '0')
|
|
|
ON CONFLICT (order_id) DO UPDATE
|
|
|
@@ -455,17 +456,16 @@ router.post('/import/logistics', authMiddleware, upload.single('file'), async (r
|
|
|
[orderId, orderNo, company || '', trackingNo]
|
|
|
);
|
|
|
|
|
|
- await pool.query('COMMIT');
|
|
|
+ await req.db.query('COMMIT');
|
|
|
results.success++;
|
|
|
} catch (err) {
|
|
|
- await pool.query('ROLLBACK');
|
|
|
+ await req.db.query('ROLLBACK');
|
|
|
results.failed++;
|
|
|
results.errors.push(`第${i + 2}行: ${err.message}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 记录导入日志
|
|
|
- await logOperation(req.user.username, req.user.role, '导入运单', `成功${results.success}条, 失败${results.failed}条`, req.ip);
|
|
|
+ await logOperation(req.db, req.user.username, req.user.role, '导入运单', `成功${results.success}条, 失败${results.failed}条`, req.ip);
|
|
|
res.json({ success: true, results });
|
|
|
} catch (err) {
|
|
|
console.error('导入运单失败:', err);
|
|
|
@@ -474,7 +474,7 @@ router.post('/import/logistics', authMiddleware, upload.single('file'), async (r
|
|
|
});
|
|
|
|
|
|
// 下载导入模板
|
|
|
-router.get('/import/template', authMiddleware, async (req, res) => {
|
|
|
+router.get('/import/template', authMiddleware, dbPool, async (req, res) => {
|
|
|
try {
|
|
|
const workbook = new ExcelJS.Workbook();
|
|
|
const sheet = workbook.addWorksheet('运单导入模板');
|
|
|
@@ -513,18 +513,17 @@ router.get('/import/template', authMiddleware, async (req, res) => {
|
|
|
|
|
|
await workbook.xlsx.write(res);
|
|
|
res.end();
|
|
|
- // 记录模板下载日志
|
|
|
- await logOperation(req.user.username, req.user.role, '下载导入模板', '下载运单导入模板', req.ip);
|
|
|
+ await logOperation(req.db, req.user.username, req.user.role, '下载导入模板', '下载运单导入模板', req.ip);
|
|
|
} catch (err) {
|
|
|
console.error('下载模板失败:', err);
|
|
|
res.status(500).json({ success: false, message: err.message });
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-// 统计数据接口(无需认证,登录页展示用)
|
|
|
-router.get('/stats', async (req, res) => {
|
|
|
+// 统计数据接口
|
|
|
+router.get('/stats', dbPool, async (req, res) => {
|
|
|
try {
|
|
|
- const result = await pool.query(`
|
|
|
+ const result = await req.db.query(`
|
|
|
SELECT
|
|
|
COUNT(*) AS total,
|
|
|
COUNT(*) FILTER (WHERE o.status = 0) AS pending,
|
|
|
@@ -552,7 +551,7 @@ router.get('/stats', async (req, res) => {
|
|
|
});
|
|
|
|
|
|
// 查询操作日志(仅admin)
|
|
|
-router.get('/logs', authMiddleware, adminOnly, async (req, res) => {
|
|
|
+router.get('/logs', authMiddleware, adminOnly, dbPool, async (req, res) => {
|
|
|
try {
|
|
|
const { page = 1, pageSize = 20, username, action, start_date, end_date } = req.query;
|
|
|
let sql = `SELECT * FROM sys_operation_log WHERE 1=1`;
|
|
|
@@ -582,14 +581,14 @@ router.get('/logs', authMiddleware, adminOnly, async (req, res) => {
|
|
|
|
|
|
// 总数
|
|
|
const countSql = sql.replace('SELECT *', 'SELECT COUNT(*) as total');
|
|
|
- const countResult = await pool.query(countSql, params);
|
|
|
+ const countResult = await req.db.query(countSql, params);
|
|
|
const total = parseInt(countResult.rows[0].total);
|
|
|
|
|
|
sql += ' ORDER BY create_time DESC';
|
|
|
sql += ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
|
params.push(pageSize, (page - 1) * pageSize);
|
|
|
|
|
|
- const result = await pool.query(sql, params);
|
|
|
+ const result = await req.db.query(sql, params);
|
|
|
res.json({ success: true, data: result.rows, total, page: parseInt(page), pageSize: parseInt(pageSize) });
|
|
|
} catch (err) {
|
|
|
console.error('查询日志失败:', err);
|