/** * 库存控制器 * * 功能:处理库存管理的业务逻辑 */ const Inventory = require('../models/Inventory'); const { validateRequired, validateBatch } = require('../utils/validator'); const { success } = require('../utils/response'); const { BusinessError, ValidationError } = require('../middleware/errorHandler'); /** * 获取库存列表(分页、筛选) * GET /api/inventory */ const getInventory = async (req, res, next) => { try { const { page = 1, pageSize = 10, category, search, low_stock_only } = req.query; const result = await Inventory.findAll({ page: parseInt(page), pageSize: parseInt(pageSize), category, search, low_stock_only: low_stock_only === 'true' || low_stock_only === '1' }); res.json(success({ list: result.list, pagination: { total: result.total, page: result.page, pageSize: result.pageSize, totalPages: Math.ceil(result.total / result.pageSize) } }, '获取库存列表成功')); } catch (err) { next(err); } }; /** * 获取单个库存项详情 * GET /api/inventory/:id */ const getInventoryById = async (req, res, next) => { try { const { id } = req.params; const item = await Inventory.findById(id); if (!item) { throw new BusinessError('库存项不存在', 404); } res.json(success(item, '获取库存详情成功')); } catch (err) { next(err); } }; /** * 创建库存项 * POST /api/inventory * 需要管理员权限 */ const createInventory = async (req, res, next) => { try { const { name, category, quantity, unit, purchase_price, supplier, expiry_date, alert_quantity } = req.body; // 数据验证 const validation = validateBatch([ validateRequired(name, '原材料名称'), validateRequired(category, '分类'), validateRequired(quantity, '数量'), validateRequired(unit, '单位'), validateRequired(purchase_price, '进货价格'), validateRequired(alert_quantity, '预警数量') ]); if (!validation.valid) { throw new ValidationError('数据验证失败', validation.errors); } // 验证数量和价格 if (parseFloat(quantity) < 0) { throw new ValidationError('数量不能为负数'); } if (parseFloat(purchase_price) <= 0) { throw new ValidationError('进货价格必须大于0'); } if (parseFloat(alert_quantity) < 0) { throw new ValidationError('预警数量不能为负数'); } // 检查名称是否已存在 const exists = await Inventory.isNameExists(name); if (exists) { throw new ValidationError('该原材料名称已存在'); } const item = await Inventory.create({ name, category, quantity: parseFloat(quantity), unit, purchase_price: parseFloat(purchase_price), supplier: supplier || null, expiry_date: expiry_date || null, alert_quantity: parseFloat(alert_quantity), operator_id: req.user.id }); res.status(201).json(success(item, '创建库存项成功')); } catch (err) { next(err); } }; /** * 更新库存项 * PUT /api/inventory/:id * 需要管理员权限 */ const updateInventory = async (req, res, next) => { try { const { id } = req.params; const { name, category, unit, purchase_price, supplier, expiry_date, alert_quantity } = req.body; // 检查库存项是否存在 const item = await Inventory.findById(id); if (!item) { throw new BusinessError('库存项不存在', 404); } // 验证数据 if (purchase_price !== undefined && parseFloat(purchase_price) <= 0) { throw new ValidationError('进货价格必须大于0'); } if (alert_quantity !== undefined && parseFloat(alert_quantity) < 0) { throw new ValidationError('预警数量不能为负数'); } // 检查名称是否已存在 if (name && name !== item.name) { const exists = await Inventory.isNameExists(name, id); if (exists) { throw new ValidationError('该原材料名称已存在'); } } const updatedItem = await Inventory.update(id, { name, category, unit, purchase_price: purchase_price !== undefined ? parseFloat(purchase_price) : undefined, supplier, expiry_date: expiry_date !== undefined ? expiry_date : undefined, alert_quantity: alert_quantity !== undefined ? parseFloat(alert_quantity) : undefined }); res.json(success(updatedItem, '更新库存项成功')); } catch (err) { next(err); } }; /** * 删除库存项 * DELETE /api/inventory/:id * 需要管理员权限 */ const deleteInventory = async (req, res, next) => { try { const { id } = req.params; // 检查库存项是否存在 const item = await Inventory.findById(id); if (!item) { throw new BusinessError('库存项不存在', 404); } await Inventory.delete(id); res.json(success(null, '删除库存项成功')); } catch (err) { next(err); } }; /** * 调整库存数量(入库/出库/调整) * POST /api/inventory/:id/adjust * 需要管理员或服务员权限 */ const adjustInventory = async (req, res, next) => { try { const { id } = req.params; const { change_type, quantity, remark } = req.body; // 数据验证 const validation = validateBatch([ validateRequired(change_type, '变更类型'), validateRequired(quantity, '数量') ]); if (!validation.valid) { throw new ValidationError('数据验证失败', validation.errors); } // 验证变更类型 const validTypes = ['in', 'out', 'adjust']; if (!validTypes.includes(change_type)) { throw new ValidationError('变更类型必须是 in、out 或 adjust'); } // 验证数量 if (parseFloat(quantity) <= 0) { throw new ValidationError('数量必须大于0'); } // 检查库存项是否存在 const item = await Inventory.findById(id); if (!item) { throw new BusinessError('库存项不存在', 404); } const updatedItem = await Inventory.adjustQuantity( id, change_type, parseFloat(quantity), req.user.id, remark || null ); const messages = { in: '入库成功', out: '出库成功', adjust: '库存调整成功' }; res.json(success(updatedItem, messages[change_type])); } catch (err) { next(err); } }; /** * 获取低库存预警列表 * GET /api/inventory/alerts/low-stock */ const getLowStockAlerts = async (req, res, next) => { try { const items = await Inventory.getLowStockItems(); res.json(success({ count: items.length, items }, '获取低库存预警成功')); } catch (err) { next(err); } }; /** * 获取即将过期的库存列表 * GET /api/inventory/alerts/expiring */ const getExpiringAlerts = async (req, res, next) => { try { const { days = 7 } = req.query; const items = await Inventory.getExpiringItems(parseInt(days)); res.json(success({ count: items.length, days: parseInt(days), items }, '获取即将过期库存成功')); } catch (err) { next(err); } }; /** * 获取已过期的库存列表 * GET /api/inventory/alerts/expired */ const getExpiredAlerts = async (req, res, next) => { try { const items = await Inventory.getExpiredItems(); res.json(success({ count: items.length, items }, '获取已过期库存成功')); } catch (err) { next(err); } }; /** * 获取库存日志 * GET /api/inventory/logs 或 GET /api/inventory/:id/logs */ const getInventoryLogs = async (req, res, next) => { try { const { id } = req.params; const { page = 1, pageSize = 20, change_type } = req.query; const result = await Inventory.getLogs(id || null, { page: parseInt(page), pageSize: parseInt(pageSize), change_type }); res.json(success({ list: result.list, pagination: { total: result.total, page: result.page, pageSize: result.pageSize, totalPages: Math.ceil(result.total / result.pageSize) } }, '获取库存日志成功')); } catch (err) { next(err); } }; /** * 获取库存分类列表 * GET /api/inventory/categories */ const getCategories = async (req, res, next) => { try { const categories = await Inventory.getCategories(); res.json(success(categories, '获取分类列表成功')); } catch (err) { next(err); } }; /** * 获取库存统计信息 * GET /api/inventory/statistics */ const getStatistics = async (req, res, next) => { try { const stats = await Inventory.getStatistics(); res.json(success(stats, '获取库存统计成功')); } catch (err) { next(err); } }; // 导出所有控制器函数 module.exports = { getInventory, getInventoryById, createInventory, updateInventory, deleteInventory, adjustInventory, getLowStockAlerts, getExpiringAlerts, getExpiredAlerts, getInventoryLogs, getCategories, getStatistics };