You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

381 lines
8.7 KiB

/**
* 菜单控制器
*
* 功能:处理菜单管理的业务逻辑
*/
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, start_date, end_date, is_active } = req.body;
// 数据验证
const validation = validateBatch([
validateRequired(name, '菜单名称'),
validateRequired(type, '菜单类型')
]);
if (!validation.valid) {
throw new ValidationError('数据验证失败', validation.errors);
}
// 验证菜单类型
const validTypes = ['lunch', 'dinner', 'holiday'];
if (!validTypes.includes(type)) {
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,
start_date: start_date || null,
end_date: end_date || null,
is_active: is_active !== undefined ? parseInt(is_active) : 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, start_date, end_date, is_active } = req.body;
// 检查菜单是否存在
const menu = await Menu.findById(id);
if (!menu) {
throw new BusinessError('菜单不存在', 404);
}
// 验证菜单类型
if (type) {
const validTypes = ['lunch', 'dinner', 'holiday'];
if (!validTypes.includes(type)) {
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,
start_date: start_date !== undefined ? start_date : undefined,
end_date: end_date !== undefined ? end_date : undefined,
is_active: is_active !== undefined ? parseInt(is_active) : 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
};