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.

368 lines
9.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 订单控制器
*
* 功能:处理订单管理的业务逻辑
*/
const Order = require('../models/Order');
const Dish = require('../models/Dish');
const User = require('../models/User');
const { validateRequired, validateBatch } = require('../utils/validator');
const { success } = require('../utils/response');
const { BusinessError, ValidationError } = require('../middleware/errorHandler');
/**
* 获取订单列表(分页、筛选)
* GET /api/orders
*/
const getOrders = async (req, res, next) => {
try {
const {
page = 1,
pageSize = 10,
customer_id,
waiter_id,
status,
order_type,
start_date,
end_date
} = req.query;
// 如果是顾客角色,只能查看自己的订单
const userId = req.user.id;
const userRole = req.user.role;
let filterCustomerId = customer_id;
if (userRole === 'customer') {
filterCustomerId = userId;
}
const result = await Order.findAll({
page: parseInt(page),
pageSize: parseInt(pageSize),
customer_id: filterCustomerId,
waiter_id,
status,
order_type,
start_date,
end_date
});
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/orders/:id
*/
const getOrderById = async (req, res, next) => {
try {
const { id } = req.params;
const order = await Order.getOrderWithItems(id);
if (!order) {
throw new BusinessError('订单不存在', 404);
}
// 权限检查:顾客只能查看自己的订单
if (req.user.role === 'customer' && order.customer_id !== req.user.id) {
throw new BusinessError('无权查看此订单', 403);
}
res.json(success(order, '获取订单详情成功'));
} catch (err) {
next(err);
}
};
/**
* 创建订单
* POST /api/orders
*/
const createOrder = async (req, res, next) => {
try {
const {
table_number,
order_type = 'dine_in',
special_request,
items // 订单明细数组
} = req.body;
// 数据验证
if (!items || !Array.isArray(items) || items.length === 0) {
throw new ValidationError('订单必须包含至少一个菜品');
}
// 验证订单类型
const validTypes = ['dine_in', 'takeaway'];
if (!validTypes.includes(order_type)) {
throw new ValidationError('订单类型必须是 dine_in 或 takeaway');
}
// 堂食必须有桌号
if (order_type === 'dine_in' && !table_number) {
throw new ValidationError('堂食订单必须提供桌号');
}
// 验证并处理订单明细
const processedItems = [];
for (const item of items) {
// 验证必填字段
const validation = validateBatch([
validateRequired(item.dish_id, '菜品ID'),
validateRequired(item.quantity, '数量')
]);
if (!validation.valid) {
throw new ValidationError('订单明细数据不完整', validation.errors);
}
// 验证数量
const quantity = parseInt(item.quantity);
if (quantity <= 0) {
throw new ValidationError('菜品数量必须大于0');
}
// 查询菜品信息
const dish = await Dish.findById(item.dish_id);
if (!dish) {
throw new BusinessError(`菜品ID ${item.dish_id} 不存在`, 404);
}
// 检查菜品是否可售
if (!dish.is_available || !dish.status) {
throw new BusinessError(`菜品 ${dish.name} 当前不可售`, 400);
}
// 添加到处理后的明细列表
processedItems.push({
dish_id: dish.id,
dish_name: dish.name,
price: parseFloat(dish.price),
quantity: quantity,
special_request: item.special_request || null
});
}
// 创建订单
// customer_id 使用当前登录用户
// waiter_id 可以由服务员设置或者为null
const order = await Order.create({
customer_id: req.user.id,
waiter_id: req.body.waiter_id || null,
table_number,
order_type,
special_request,
discount_amount: 0
}, processedItems);
// 获取完整订单信息(含明细)
const fullOrder = await Order.getOrderWithItems(order.id);
res.status(201).json(success(fullOrder, '创建订单成功'));
} catch (err) {
next(err);
}
};
/**
* 更新订单状态
* PUT /api/orders/:id/status
* 需要服务员或管理员权限
*/
const updateOrderStatus = async (req, res, next) => {
try {
const { id } = req.params;
const { status } = req.body;
// 验证状态值
const validStatuses = ['pending', 'confirmed', 'cooking', 'served', 'paid', 'cancelled'];
if (!validStatuses.includes(status)) {
throw new ValidationError('无效的订单状态');
}
// 检查订单是否存在
const order = await Order.findById(id);
if (!order) {
throw new BusinessError('订单不存在', 404);
}
// 检查状态流转是否合理
if (order.status === 'cancelled') {
throw new BusinessError('已取消的订单不能更改状态', 400);
}
if (order.status === 'paid' && status !== 'cancelled') {
throw new BusinessError('已支付的订单不能更改状态', 400);
}
const updatedOrder = await Order.updateStatus(id, status);
res.json(success(updatedOrder, '更新订单状态成功'));
} catch (err) {
next(err);
}
};
/**
* 取消订单
* PUT /api/orders/:id/cancel
*/
const cancelOrder = async (req, res, next) => {
try {
const { id } = req.params;
// 检查订单是否存在
const order = await Order.findById(id);
if (!order) {
throw new BusinessError('订单不存在', 404);
}
// 权限检查:顾客只能取消自己的订单
if (req.user.role === 'customer' && order.customer_id !== req.user.id) {
throw new BusinessError('无权取消此订单', 403);
}
// 检查订单状态
if (order.status === 'cancelled') {
throw new BusinessError('订单已经取消', 400);
}
if (order.status === 'paid') {
throw new BusinessError('已支付的订单不能取消', 400);
}
if (order.status === 'served') {
throw new BusinessError('已上菜的订单不能取消', 400);
}
const cancelledOrder = await Order.cancel(id);
res.json(success(cancelledOrder, '取消订单成功'));
} catch (err) {
next(err);
}
};
/**
* 应用折扣到订单
* PUT /api/orders/:id/discount
* 需要服务员或管理员权限
*/
const applyDiscount = async (req, res, next) => {
try {
const { id } = req.params;
const { discount_amount } = req.body;
// 验证折扣金额
if (discount_amount === undefined || discount_amount === null) {
throw new ValidationError('请提供折扣金额');
}
const discountValue = parseFloat(discount_amount);
if (discountValue < 0) {
throw new ValidationError('折扣金额不能为负数');
}
// 检查订单是否存在
const order = await Order.findById(id);
if (!order) {
throw new BusinessError('订单不存在', 404);
}
// 检查订单状态
if (order.status === 'paid') {
throw new BusinessError('已支付的订单不能修改折扣', 400);
}
if (order.status === 'cancelled') {
throw new BusinessError('已取消的订单不能修改折扣', 400);
}
// 验证折扣金额不能超过总金额
if (discountValue > order.total_amount) {
throw new ValidationError('折扣金额不能超过订单总金额');
}
const updatedOrder = await Order.updateAmount(id, discountValue);
res.json(success(updatedOrder, '应用折扣成功'));
} catch (err) {
next(err);
}
};
/**
* 获取订单统计数据
* GET /api/orders/statistics
* 需要管理员权限
*/
const getOrderStatistics = async (req, res, next) => {
try {
const { start_date, end_date, status } = req.query;
const statistics = await Order.getStatistics({
start_date,
end_date,
status
});
const statusStats = await Order.getStatusStatistics();
res.json(success({
overall: statistics,
by_status: statusStats
}, '获取订单统计成功'));
} catch (err) {
next(err);
}
};
/**
* 获取我的订单(当前登录用户)
* GET /api/orders/my
*/
const getMyOrders = async (req, res, next) => {
try {
const { page = 1, pageSize = 10, status } = req.query;
const result = await Order.findAll({
page: parseInt(page),
pageSize: parseInt(pageSize),
customer_id: req.user.id,
status
});
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);
}
};
// 导出所有控制器函数
module.exports = {
getOrders,
getOrderById,
createOrder,
updateOrderStatus,
cancelOrder,
applyDiscount,
getOrderStatistics,
getMyOrders
};