256 lines
8.5 KiB
JavaScript
256 lines
8.5 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const jwt = require('jsonwebtoken');
|
|
const rateLimit = require('express-rate-limit');
|
|
const Rider = require('../models/Rider');
|
|
const Order = require('../models/Order');
|
|
const { comparePassword } = require('../utils/password');
|
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
|
const { validate } = require('../middleware/validate');
|
|
const { schemas } = require('../middleware/validate');
|
|
|
|
// 登录限流
|
|
const loginLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 100,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
message: { success: false, message: '登录尝试过于频繁,请稍后再试' }
|
|
});
|
|
|
|
// 骑手登录
|
|
router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => {
|
|
try {
|
|
const { phone, password } = req.body;
|
|
const trimmedPhone = phone ? phone.trim() : '';
|
|
const trimmedPassword = password ? password.trim() : '';
|
|
|
|
const rider = await Rider.findOne({ phone: trimmedPhone }).select('+password');
|
|
if (!rider) {
|
|
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
|
}
|
|
|
|
const isMatch = await comparePassword(trimmedPassword, rider.password);
|
|
if (!isMatch) {
|
|
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
|
}
|
|
|
|
const token = jwt.sign(
|
|
{ id: rider._id, role: rider.role, type: 'rider', phone: rider.phone, jti: Math.random().toString(36) },
|
|
process.env.JWT_SECRET,
|
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
id: rider._id,
|
|
riderId: rider.riderId,
|
|
name: rider.name,
|
|
phone: rider.phone,
|
|
status: rider.status,
|
|
role: rider.role,
|
|
rating: rider.rating,
|
|
totalOrders: rider.totalOrders,
|
|
totalIncome: rider.totalIncome,
|
|
token
|
|
}
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// ===== 固定路由(必须放在 /:id 之前) =====
|
|
|
|
// 获取骑手统计
|
|
router.get('/stats', authMiddleware, requireRole('rider'), async (req, res) => {
|
|
try {
|
|
const rider = await Rider.findById(req.user.id);
|
|
if (!rider) {
|
|
return res.status(404).json({ success: false, message: '骑手不存在' });
|
|
}
|
|
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const todayOrders = await Order.countDocuments({
|
|
rider: req.user.id,
|
|
status: '已完成',
|
|
actualEndDate: { $gte: today }
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
todayOrders,
|
|
monthIncome: rider.totalIncome || 0,
|
|
completedOrders: rider.totalOrders || 0
|
|
}
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 获取可接订单
|
|
router.get('/orders/available', authMiddleware, requireRole('rider'), async (req, res) => {
|
|
try {
|
|
const orders = await Order.find({ rider: { $exists: false }, status: '待支付' })
|
|
.populate('customer', 'name phone')
|
|
.populate('vehicle', 'vehicleId model color')
|
|
.sort({ createdAt: -1 })
|
|
.limit(50);
|
|
res.json({ success: true, data: orders });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 骑手接单
|
|
router.post('/orders/:id/accept', authMiddleware, requireRole('rider'), async (req, res) => {
|
|
try {
|
|
const order = await Order.findById(req.params.id);
|
|
if (!order) {
|
|
return res.status(404).json({ success: false, message: '订单不存在' });
|
|
}
|
|
if (order.rider) {
|
|
return res.status(400).json({ success: false, message: '该订单已被接单' });
|
|
}
|
|
order.rider = req.user.id;
|
|
order.status = '进行中';
|
|
await order.save();
|
|
|
|
await Rider.findByIdAndUpdate(req.user.id, { $inc: { totalOrders: 1 } });
|
|
|
|
res.json({ success: true, data: order });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 骑手完成配送
|
|
router.post('/orders/:id/complete', authMiddleware, requireRole('rider'), async (req, res) => {
|
|
try {
|
|
const order = await Order.findOne({ _id: req.params.id, rider: req.user.id });
|
|
if (!order) {
|
|
return res.status(404).json({ success: false, message: '订单不存在或无权操作' });
|
|
}
|
|
order.status = '已完成';
|
|
order.actualEndDate = new Date();
|
|
await order.save();
|
|
|
|
await Rider.findByIdAndUpdate(req.user.id, { $inc: { totalIncome: order.totalAmount } });
|
|
|
|
res.json({ success: true, data: order });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 骑手取消接单
|
|
router.post('/orders/:id/cancel', authMiddleware, requireRole('rider'), async (req, res) => {
|
|
try {
|
|
const order = await Order.findOne({ _id: req.params.id, rider: req.user.id });
|
|
if (!order) {
|
|
return res.status(404).json({ success: false, message: '订单不存在或无权操作' });
|
|
}
|
|
order.rider = null;
|
|
order.status = '待支付';
|
|
await order.save();
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// ===== 参数路由(放在最后) =====
|
|
|
|
// 获取空闲车辆(公开接口,无需授权)
|
|
router.get('/vehicles', async (req, res) => {
|
|
try {
|
|
const Store = require('../models/Store');
|
|
const Vehicle = require('../models/Vehicle');
|
|
// 只返回关联门店已审批通过的空闲车辆
|
|
const vehicles = await Vehicle.find({ status: '空闲' })
|
|
.populate('storeId', 'name address')
|
|
.lean();
|
|
res.json({ success: true, data: vehicles });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 获取当前骑手的订单列表
|
|
router.get('/orders', authMiddleware, requireRole('rider'), async (req, res) => {
|
|
try {
|
|
const orders = await Order.find({ customer: req.user.id })
|
|
.populate('vehicle', 'model color vehicleId')
|
|
.populate('storeId', 'name')
|
|
.sort({ createdAt: -1 });
|
|
res.json({ success: true, data: orders });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 获取骑手信息
|
|
router.get('/:id', authMiddleware, async (req, res) => {
|
|
try {
|
|
const isSelf = req.params.id === req.user.id;
|
|
const isPrivileged = ['admin', 'store'].includes(req.user.role);
|
|
if (!isSelf && !isPrivileged) {
|
|
return res.status(403).json({ success: false, message: '无权查看该骑手信息' });
|
|
}
|
|
const rider = await Rider.findById(req.params.id);
|
|
if (!rider) {
|
|
return res.status(404).json({ success: false, message: '骑手不存在' });
|
|
}
|
|
res.json({ success: true, data: rider });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 更新骑手信息
|
|
router.put('/:id', authMiddleware, async (req, res) => {
|
|
try {
|
|
const isSelf = req.params.id === req.user.id;
|
|
const isPrivileged = ['admin', 'store'].includes(req.user.role);
|
|
if (!isSelf && !isPrivileged) {
|
|
return res.status(403).json({ success: false, message: '无权修改该骑手信息' });
|
|
}
|
|
const { password, ...safeBody } = req.body;
|
|
if (req.body.role && !isPrivileged) {
|
|
return res.status(403).json({ success: false, message: '无权修改角色' });
|
|
}
|
|
const updateData = isPrivileged ? req.body : safeBody;
|
|
const rider = await Rider.findByIdAndUpdate(req.params.id, updateData, { new: true, runValidators: true });
|
|
if (!rider) {
|
|
return res.status(404).json({ success: false, message: '骑手不存在' });
|
|
}
|
|
res.json({ success: true, data: rider });
|
|
} catch (error) {
|
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
// 获取骑手的订单
|
|
router.get('/:id/orders', authMiddleware, async (req, res) => {
|
|
try {
|
|
const isSelf = req.params.id === req.user.id;
|
|
const isPrivileged = ['admin', 'store'].includes(req.user.role);
|
|
if (!isSelf && !isPrivileged) {
|
|
return res.status(403).json({ success: false, message: '无权查看该骑手的订单' });
|
|
}
|
|
const orders = await Order.find({ rider: req.params.id })
|
|
.populate('customer', 'name phone')
|
|
.populate('vehicle', 'vehicleId model color')
|
|
.sort({ createdAt: -1 });
|
|
res.json({ success: true, data: orders });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|