Files
ai-xiaocai/server/routes/purchases.js
2026-07-28 17:51:14 +08:00

135 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 采购相关路由
const express = require('express');
const { authMiddleware, adminOnly } = require('../middleware/auth');
const { broadcastToRoom } = require('../ws');
const db = require('../db');
const { timestamp } = require('../utils');
const router = express.Router();
router.get('/rooms/:roomId/purchases', authMiddleware, (req, res) => {
const purchases = db.prepare('SELECT * FROM purchases WHERE room_id = ? ORDER BY created_at DESC').all(req.params.roomId);
res.json(purchases);
});
router.get('/purchases/:id', authMiddleware, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
const history = db.prepare('SELECT * FROM purchase_history WHERE purchase_id = ? ORDER BY id ASC').all(req.params.id);
const attachments = db.prepare('SELECT * FROM purchase_attachments WHERE purchase_id = ?').all(req.params.id);
res.json({ ...pur, history, attachments });
});
// 更新采购记录
router.put('/purchases/:id', authMiddleware, (req, res) => {
const old = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!old) return res.status(404).json({ error: '未找到' });
const now = timestamp();
const username = req.user.username;
const { item, quantity, unit_price, freight, payment_method, invoice_type, status, applicant, remarks, created_at } = req.body;
const newVals = {
item: item !== undefined ? String(item) : old.item,
quantity: quantity !== undefined ? Number(quantity) : old.quantity,
unit_price: unit_price !== undefined ? Number(unit_price) : old.unit_price,
freight: freight !== undefined ? Number(freight) : old.freight,
payment_method: payment_method !== undefined ? String(payment_method) : old.payment_method,
invoice_type: invoice_type !== undefined ? String(invoice_type) : old.invoice_type,
status: status !== undefined ? String(status) : old.status,
applicant: applicant !== undefined ? String(applicant) : old.applicant,
remarks: remarks !== undefined ? String(remarks) : old.remarks,
created_at: created_at !== undefined ? String(created_at) : old.created_at
};
newVals.amount = newVals.quantity * newVals.unit_price + newVals.freight;
const fieldLabels = {
item: '物品', quantity: '数量', unit_price: '单价', freight: '邮费', amount: '金额',
payment_method: '付款方式', invoice_type: '发票', status: '状态', applicant: '申请人', remarks: '备注', created_at: '时间'
};
const changes = [];
for (const [field, label] of Object.entries(fieldLabels)) {
if (String(newVals[field]) !== String(old[field])) {
changes.push(label + ': ' + (old[field] || '空') + ' -> ' + newVals[field]);
db.prepare('UPDATE purchases SET ' + field + ' = ? WHERE id = ?').run(newVals[field], old.id);
}
}
if (changes.length > 0) {
db.prepare('UPDATE purchases SET updated_at = ? WHERE id = ?').run(now, old.id);
db.prepare('INSERT INTO purchase_history (purchase_id, action, user, timestamp) VALUES (?,?,?,?)').run(old.id, '更新:' + changes.join(''), username, now);
broadcastToRoom(old.room_id, { type: 'purchase_updated' });
res.json({ success: true, changes });
} else {
res.json({ success: true, changes: [] });
}
});
// 删除采购记录(管理员)
router.delete('/purchases/:id', authMiddleware, adminOnly, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
db.prepare('DELETE FROM purchase_attachments WHERE purchase_id = ?').run(pur.id);
db.prepare('DELETE FROM purchase_history WHERE purchase_id = ?').run(pur.id);
db.prepare('DELETE FROM purchases WHERE id = ?').run(pur.id);
broadcastToRoom(pur.room_id, { type: 'purchase_updated' });
res.json({ success: true });
});
// 管理附件
router.post('/purchases/:id/attachments', authMiddleware, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
const now = timestamp();
const username = req.user.username;
const { file_paths } = req.body;
if (!file_paths || !file_paths.length) return res.status(400).json({ error: '缺少文件路径' });
const insertAttach = db.prepare('INSERT INTO purchase_attachments (purchase_id, file_path, uploaded_by, timestamp) VALUES (?,?,?,?)');
file_paths.forEach(fp => insertAttach.run(pur.id, fp, username, now));
db.prepare('INSERT INTO purchase_history (purchase_id, action, user, timestamp) VALUES (?,?,?,?)').run(pur.id, '添加附件:' + file_paths.length + ' 个', username, now);
broadcastToRoom(pur.room_id, { type: 'purchase_updated' });
res.json({ success: true });
});
router.delete('/purchases/:id/attachments', authMiddleware, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
const username = req.user.username;
const { file_path } = req.body;
if (!file_path) return res.status(400).json({ error: '缺少文件路径' });
db.prepare('DELETE FROM purchase_attachments WHERE purchase_id = ? AND file_path = ?').run(pur.id, file_path);
db.prepare('INSERT INTO purchase_history (purchase_id, action, user, timestamp) VALUES (?,?,?,?)').run(pur.id, '删除附件:' + file_path.split('/').pop(), username, timestamp());
broadcastToRoom(pur.room_id, { type: 'purchase_updated' });
res.json({ success: true });
});
router.get('/summary', authMiddleware, (req, res) => {
const username = req.user.username;
const isAdmin = req.user.isAdmin;
let rooms;
if (isAdmin) {
rooms = db.prepare('SELECT id, name FROM rooms').all();
} else {
rooms = db.prepare("SELECT id, name FROM rooms WHERE white_list = '' OR (',' || white_list || ',' LIKE ?)").all('%,' + username + ',%');
}
const summary = rooms.map(room => {
const stats = db.prepare('SELECT COUNT(*) as count, SUM(amount) as total FROM purchases WHERE room_id = ?').get(room.id);
return { room_id: room.id, room_name: room.name, purchase_count: stats.count || 0, total_amount: stats.total || 0 };
});
res.json(summary);
});
router.get('/rooms/:roomId/purchases/export', authMiddleware, (req, res) => {
const purchases = db.prepare('SELECT * FROM purchases WHERE room_id = ?').all(req.params.roomId);
let csv = '时间,事项,数量,单价,金额,邮费,付款方式,发票类型,状态,申请人,备注\n';
purchases.forEach(p => {
csv += p.created_at + ',"' + (p.item||'') + '",' + p.quantity + ',' + p.unit_price + ',' + p.amount + ',' + p.freight + ',"' + (p.payment_method||'') + '","' + (p.invoice_type||'') + '","' + p.status + '","' + p.applicant + '","' + (p.remarks||'') + '"\n';
});
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', 'attachment; filename="purchases-' + req.params.roomId + '.csv"');
res.send('\uFEFF' + csv);
});
module.exports = router;