|
|
/**
|
|
|
* 订单控制器
|
|
|
*
|
|
|
* 功能:处理订单管理的业务逻辑
|
|
|
*/
|
|
|
|
|
|
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
|
|
|
};
|