forked from p4b8lshcr/ChefTronic
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
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
|
|
};
|