/** * 菜单控制器 * * 功能:处理菜单管理的业务逻辑 */ const Menu = require('../models/Menu'); const Dish = require('../models/Dish'); const { validateRequired, validateBatch } = require('../utils/validator'); const { success } = require('../utils/response'); const { BusinessError, ValidationError } = require('../middleware/errorHandler'); /** * 获取菜单列表(分页、筛选) * GET /api/menus */ const getMenus = async (req, res, next) => { try { const { page = 1, pageSize = 10, type, is_active } = req.query; const result = await Menu.findAll({ page: parseInt(page), pageSize: parseInt(pageSize), type, is_active: is_active !== undefined ? parseInt(is_active) : undefined }); 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/menus/:id */ const getMenuById = async (req, res, next) => { try { const { id } = req.params; const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } res.json(success(menu, '获取菜单详情成功')); } catch (err) { next(err); } }; /** * 创建菜单 * POST /api/menus * 需要管理员权限 */ const createMenu = async (req, res, next) => { try { const { name, type, menu_type, start_date, end_date, is_active } = req.body; // Use menu_type if provided, otherwise use type const menuType = menu_type || type; // 数据验证 const validation = validateBatch([ validateRequired(name, '菜单名称'), validateRequired(menuType, '菜单类型') ]); if (!validation.valid) { throw new ValidationError('数据验证失败', validation.errors); } // 验证菜单类型 const validTypes = ['lunch', 'dinner', 'holiday']; if (!validTypes.includes(menuType)) { throw new ValidationError('菜单类型必须是 lunch、dinner 或 holiday'); } // 验证日期范围 if (start_date && end_date && new Date(start_date) > new Date(end_date)) { throw new ValidationError('开始日期不能晚于结束日期'); } const menu = await Menu.create({ name, type: menuType, start_date: start_date || null, end_date: end_date || null, is_active: is_active !== undefined ? (is_active ? 1 : 0) : 1 }); res.status(201).json(success(menu, '创建菜单成功')); } catch (err) { next(err); } }; /** * 更新菜单 * PUT /api/menus/:id * 需要管理员权限 */ const updateMenu = async (req, res, next) => { try { const { id } = req.params; const { name, type, menu_type, start_date, end_date, is_active } = req.body; // Use menu_type if provided, otherwise use type const menuType = menu_type || type; // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } // 验证菜单类型 if (menuType) { const validTypes = ['lunch', 'dinner', 'holiday']; if (!validTypes.includes(menuType)) { throw new ValidationError('菜单类型必须是 lunch、dinner 或 holiday'); } } // 验证日期范围 const newStartDate = start_date || menu.start_date; const newEndDate = end_date || menu.end_date; if (newStartDate && newEndDate && new Date(newStartDate) > new Date(newEndDate)) { throw new ValidationError('开始日期不能晚于结束日期'); } const updatedMenu = await Menu.update(id, { name, type: menuType, start_date: start_date !== undefined ? start_date : undefined, end_date: end_date !== undefined ? end_date : undefined, is_active: is_active !== undefined ? (is_active ? 1 : 0) : undefined }); res.json(success(updatedMenu, '更新菜单成功')); } catch (err) { next(err); } }; /** * 删除菜单 * DELETE /api/menus/:id * 需要管理员权限 */ const deleteMenu = async (req, res, next) => { try { const { id } = req.params; // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } await Menu.delete(id); res.json(success(null, '删除菜单成功')); } catch (err) { next(err); } }; /** * 获取菜单中的菜品列表 * GET /api/menus/:id/dishes */ const getMenuDishes = async (req, res, next) => { try { const { id } = req.params; // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } const dishes = await Menu.getMenuDishes(id); res.json(success({ menu: { id: menu.id, name: menu.name, type: menu.type, start_date: menu.start_date, end_date: menu.end_date, is_active: menu.is_active }, dishes }, '获取菜单菜品成功')); } catch (err) { next(err); } }; /** * 为菜单添加菜品 * POST /api/menus/:id/dishes * 需要管理员权限 */ const addDishToMenu = async (req, res, next) => { try { const { id } = req.params; const { dish_id, special_price } = req.body; // 数据验证 const validation = validateBatch([ validateRequired(dish_id, '菜品ID') ]); if (!validation.valid) { throw new ValidationError('数据验证失败', validation.errors); } // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } // 检查菜品是否存在 const dish = await Dish.findById(dish_id); if (!dish) { throw new BusinessError('菜品不存在', 404); } // 验证特价 if (special_price !== undefined && special_price !== null) { if (parseFloat(special_price) <= 0) { throw new ValidationError('特价必须大于0'); } } const addedDish = await Menu.addDish( id, dish_id, special_price ? parseFloat(special_price) : null ); res.status(201).json(success(addedDish, '添加菜品到菜单成功')); } catch (err) { next(err); } }; /** * 从菜单移除菜品 * DELETE /api/menus/:id/dishes/:dishId * 需要管理员权限 */ const removeDishFromMenu = async (req, res, next) => { try { const { id, dishId } = req.params; // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } await Menu.removeDish(id, dishId); res.json(success(null, '从菜单移除菜品成功')); } catch (err) { next(err); } }; /** * 更新菜单中菜品的特价 * PUT /api/menus/:id/dishes/:dishId/price * 需要管理员权限 */ const updateMenuDishPrice = async (req, res, next) => { try { const { id, dishId } = req.params; const { special_price } = req.body; // 验证特价 if (special_price !== undefined && special_price !== null) { if (parseFloat(special_price) <= 0) { throw new ValidationError('特价必须大于0'); } } // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } await Menu.updateDishPrice( id, dishId, special_price ? parseFloat(special_price) : null ); res.json(success(null, '更新菜品特价成功')); } catch (err) { next(err); } }; /** * 批量添加菜品到菜单 * POST /api/menus/:id/dishes/batch * 需要管理员权限 */ const addDishesToMenu = async (req, res, next) => { try { const { id } = req.params; const { dishes } = req.body; // 数据验证 if (!Array.isArray(dishes) || dishes.length === 0) { throw new ValidationError('请提供菜品数组'); } // 检查菜单是否存在 const menu = await Menu.findById(id); if (!menu) { throw new BusinessError('菜单不存在', 404); } const successCount = await Menu.addDishes(id, dishes); res.json(success({ total: dishes.length, success: successCount, failed: dishes.length - successCount }, `成功添加${successCount}个菜品到菜单`)); } catch (err) { next(err); } }; /** * 获取当前有效的菜单 * GET /api/menus/active */ const getActiveMenus = async (req, res, next) => { try { const { type } = req.query; const menus = await Menu.getActiveMenus(type); res.json(success(menus, '获取有效菜单成功')); } catch (err) { next(err); } }; // 导出所有控制器函数 module.exports = { getMenus, getMenuById, createMenu, updateMenu, deleteMenu, getMenuDishes, addDishToMenu, removeDishFromMenu, updateMenuDishPrice, addDishesToMenu, getActiveMenus };