e-scooter-rental-system/server/routes/orders.js

241 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const router = express.Router();
const Order = require('../models/Order');
const Vehicle = require('../models/Vehicle');
const Customer = require('../models/Customer');
const { authMiddleware, requireRole } = require('../middleware/auth');
const { validate } = require('../middleware/validate');
const { schemas } = require('../middleware/validate');
// ─── 公开列表查询登录即可admin 可看全部customer 只能看自己的) ───
router.get('/', authMiddleware, async (req, res) => {
try {
const filter = {};
if (req.query.status) filter.status = req.query.status;
// 非 admin 角色只能看自己的订单
if (req.user.role === 'customer') {
// 查找当前 rider 对应的客户手机号(需关联 Customer 表)
// 此处通过 rider id 查找(假设订单中 rider 字段关联骑手)
filter.rider = req.user.id;
} else if (req.user.role === 'store') {
// store 角色可按门店过滤(前端传 storeId
if (req.query.storeId) filter.storeId = req.query.storeId;
}
const orders = await Order.find(filter)
.populate('customer', 'name phone')
.populate('vehicle', 'model vehicleId');
res.json({ success: true, data: orders });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// 获取单个订单(登录即可,检查是否本人或 admin/store
router.get('/:id', authMiddleware, async (req, res) => {
try {
const order = await Order.findById(req.params.id)
.populate('customer')
.populate('vehicle');
if (!order) return res.status(404).json({ success: false, message: '订单不存在' });
// 权限检查:本人骑手 或 admin/store
const isOwner = order.rider?.toString() === req.user.id;
if (req.user.role !== 'admin' && req.user.role !== 'store' && !isOwner) {
return res.status(403).json({ success: false, message: '权限不足' });
}
res.json({ success: true, data: order });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// 创建订单store 或 admin
router.post('/', authMiddleware, requireRole('admin', 'store'), validate(schemas.order), async (req, res) => {
try {
const {
customerName, customer,
vehicleId, vehicle,
contractMonths, startDate, endDate, rentalFee, deposit, orderType
} = req.body;
const vehicleObjectId = vehicle || vehicleId;
// 解析客户
let customerObjectId = customer;
if (!customerObjectId && customerName) {
let customerDoc = await Customer.findOne({ name: customerName });
if (!customerDoc) {
customerDoc = await Customer.create({
name: customerName,
phone: '00000000000',
customerId: `CUST${Date.now()}`
});
}
customerObjectId = customerDoc._id;
}
const vehicleDoc = await Vehicle.findById(vehicleObjectId);
if (!vehicleDoc) return res.status(404).json({ success: false, message: '车辆不存在' });
if (vehicleDoc.status !== '空闲') {
return res.status(400).json({ success: false, message: '车辆不可用,当前状态:' + vehicleDoc.status });
}
const start = startDate ? new Date(startDate) : new Date();
let end = endDate ? new Date(endDate) : null;
const months = Number(contractMonths) || 0;
if (!end && months > 0) {
end = new Date(start);
end.setMonth(end.getMonth() + months);
}
const fee = Number(rentalFee) || 0;
const depositAmt = Number(deposit) || 0;
const totalAmount = fee + depositAmt;
const order = new Order({
customer: customerObjectId,
vehicle: vehicleObjectId,
startDate: start,
endDate: end || new Date(start.getTime() + 30 * 24 * 60 * 60 * 1000),
rentalFee: fee,
deposit: depositAmt,
totalAmount,
status: '进行中'
});
await order.save();
vehicleDoc.status = '在租';
vehicleDoc.isRented = true;
vehicleDoc.currentOrderId = order._id;
await vehicleDoc.save();
if (customerObjectId) {
const cust = await Customer.findById(customerObjectId);
if (cust) {
cust.totalRentals += 1;
cust.currentRentals = (cust.currentRentals || 0) + 1;
cust.totalSpent = (cust.totalSpent || 0) + totalAmount;
await cust.save();
}
}
res.status(201).json({ success: true, data: order });
} catch (error) {
res.status(400).json({ success: false, message: error.message });
}
});
// 更新订单admin 或 store
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
try {
const order = await Order.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
if (!order) return res.status(404).json({ success: false, message: '订单不存在' });
res.json({ success: true, data: order });
} catch (error) {
res.status(400).json({ success: false, message: error.message });
}
});
// 结束订单admin 或 store
router.patch('/:id/complete', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
try {
const order = await Order.findById(req.params.id);
if (!order) return res.status(404).json({ success: false, message: '订单不存在' });
order.status = '已完成';
order.actualEndDate = new Date();
await order.save();
const vehicle = await Vehicle.findById(order.vehicle);
if (vehicle) {
vehicle.status = '空闲';
vehicle.isRented = false;
vehicle.currentOrderId = null;
vehicle.totalRentDays += Math.ceil((new Date(order.endDate) - new Date(order.startDate)) / (1000 * 60 * 60 * 24));
await vehicle.save();
}
const customer = await Customer.findById(order.customer);
if (customer) {
customer.currentRentals -= 1;
await customer.save();
}
res.json({ success: true, data: order });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// 取消订单admin 或 store
router.patch('/:id/cancel', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
try {
const order = await Order.findById(req.params.id);
if (!order) return res.status(404).json({ success: false, message: '订单不存在' });
if (order.status === '已完成' || order.status === '已取消') {
return res.status(400).json({ success: false, message: '当前状态无法取消' });
}
order.status = '已取消';
order.actualEndDate = new Date();
await order.save();
const vehicle = await Vehicle.findById(order.vehicle);
if (vehicle) {
vehicle.status = '空闲';
vehicle.isRented = false;
vehicle.currentOrderId = null;
await vehicle.save();
}
const customer = await Customer.findById(order.customer);
if (customer) {
customer.currentRentals = Math.max((customer.currentRentals || 0) - 1, 0);
await customer.save();
}
res.json({ success: true, data: order });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// 逾期订单admin 或 store
router.get('/status/overdue', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
try {
const now = new Date();
const orders = await Order.find({
status: '进行中',
endDate: { $lt: now }
})
.populate('customer', 'name phone')
.populate('vehicle', 'model vehicleId');
for (const order of orders) {
const overdueDays = Math.ceil((now - order.endDate) / (1000 * 60 * 60 * 24));
order.overdueDays = overdueDays;
order.overdueFee = overdueDays * order.rentalFee * 0.1;
order.status = '逾期';
await order.save();
}
res.json({ success: true, data: orders });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// 按状态筛选admin 或 store
router.get('/status/:status', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
try {
const orders = await Order.find({ status: req.params.status })
.populate('customer', 'name phone')
.populate('vehicle', 'model vehicleId');
res.json({ success: true, data: orders });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
module.exports = router;