Files
yunrui_asset/backend/routes/consumableRecords.js
T
zhang1106 568f9c7db8 feat(consumables): 添加耗材管理功能模块
添加耗材管理相关功能,包括:
1. 后端模型、路由和控制器
2. 前端页面和组件
3. 耗材分类管理
4. 耗材出入库记录
5. 耗材统计和日志功能
6. 集成到现有系统菜单
2025-12-24 16:44:27 +08:00

189 lines
4.8 KiB
JavaScript

const express = require('express');
const router = express.Router();
const { Op } = require('sequelize');
const { sequelize } = require('../db');
const Consumable = require('../models/Consumable');
const ConsumableRecord = require('../models/ConsumableRecord');
const ConsumableLog = require('../models/ConsumableLog');
router.get('/', async (req, res) => {
try {
const { consumableId, type, startDate, endDate, page = 1, pageSize = 10 } = req.query;
const offset = (page - 1) * pageSize;
const where = {};
if (consumableId) {
where.consumableId = consumableId;
}
if (type && type !== 'all') {
where.type = type;
}
if (startDate && endDate) {
where.createdAt = {
[Op.between]: [new Date(startDate), new Date(endDate)]
};
} else if (startDate) {
where.createdAt = { [Op.gte]: new Date(startDate) };
} else if (endDate) {
where.createdAt = { [Op.lte]: new Date(endDate) };
}
const { count, rows } = await ConsumableRecord.findAndCountAll({
where,
include: [
{ model: Consumable, attributes: ['name', 'category', 'unit'] }
],
offset,
limit: parseInt(pageSize),
order: [['createdAt', 'DESC']]
});
res.json({
total: count,
records: rows,
page: parseInt(page),
pageSize: parseInt(pageSize)
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.post('/', async (req, res) => {
const transaction = await sequelize.transaction();
try {
const { consumableId, type, quantity, operator, reason, recipient, notes } = req.body;
const consumable = await Consumable.findByPk(consumableId);
if (!consumable) {
await transaction.rollback();
return res.status(404).json({ error: '耗材不存在' });
}
const previousStock = consumable.currentStock;
let newStock;
if (type === 'in') {
newStock = previousStock + quantity;
} else if (type === 'out') {
if (previousStock < quantity) {
await transaction.rollback();
return res.status(400).json({ error: '库存不足' });
}
newStock = previousStock - quantity;
} else {
await transaction.rollback();
return res.status(400).json({ error: '操作类型无效' });
}
await consumable.update({ currentStock: newStock }, { transaction });
const record = await ConsumableRecord.create({
consumableId,
type,
quantity,
previousStock,
currentStock: newStock,
operator,
reason,
recipient,
notes
}, { transaction });
await ConsumableLog.create({
consumableId,
consumableName: consumable.name,
operationType: type,
quantity: type === 'in' ? parseFloat(quantity) : -parseFloat(quantity),
previousStock,
currentStock: newStock,
operator,
reason,
notes
}, { transaction });
await transaction.commit();
res.status(201).json({
record,
consumable: {
previousStock,
currentStock: newStock
}
});
} catch (error) {
await transaction.rollback();
res.status(400).json({ error: error.message });
}
});
router.get('/statistics', async (req, res) => {
try {
const { startDate, endDate } = req.query;
const dateWhere = {};
if (startDate && endDate) {
dateWhere.createdAt = {
[Op.between]: [new Date(startDate), new Date(endDate)]
};
}
const inCount = await ConsumableRecord.count({
where: { ...dateWhere, type: 'in' }
});
const outCount = await ConsumableRecord.count({
where: { ...dateWhere, type: 'out' }
});
const inQuantity = await ConsumableRecord.sum('quantity', {
where: { ...dateWhere, type: 'in' }
}) || 0;
const outQuantity = await ConsumableRecord.sum('quantity', {
where: { ...dateWhere, type: 'out' }
}) || 0;
const byType = await ConsumableRecord.findAll({
where: dateWhere,
attributes: [
'type',
[sequelize.fn('SUM', sequelize.col('quantity')), 'totalQuantity'],
[sequelize.fn('COUNT', '*'), 'count']
],
group: ['type']
});
const recentRecords = await ConsumableRecord.findAll({
where: dateWhere,
include: [
{ model: Consumable, attributes: ['name', 'category'] }
],
order: [['createdAt', 'DESC']],
limit: 10
});
res.json({
inCount,
outCount,
inQuantity,
outQuantity,
netQuantity: inQuantity - outQuantity,
byType: byType.map(item => ({
type: item.type,
totalQuantity: item.dataValues.totalQuantity,
count: item.dataValues.count
})),
recentRecords
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;