130 lines
4.6 KiB
JavaScript
130 lines
4.6 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: 20,
|
||
standardHeaders: true,
|
||
legacyHeaders: false,
|
||
message: { success: false, message: '登录尝试过于频繁,请15分钟后再试' }
|
||
});
|
||
|
||
// 骑手登录(无需鉴权)
|
||
router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => {
|
||
try {
|
||
const { phone, password } = req.body;
|
||
|
||
// 用 select(false) 主动拉取 password 字段
|
||
const rider = await Rider.findOne({ phone }).select('+password');
|
||
if (!rider) {
|
||
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
||
}
|
||
|
||
// bcrypt 比对密码
|
||
const isMatch = await comparePassword(password, rider.password);
|
||
if (!isMatch) {
|
||
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
||
}
|
||
|
||
// 签发 JWT(包含 id、role、jti)
|
||
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: "服务器内部错误" });
|
||
}
|
||
});
|
||
|
||
// 获取骑手信息(需登录,且只能查看自己;admin/store 可查看任意骑手)
|
||
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: "服务器内部错误" });
|
||
}
|
||
});
|
||
|
||
// 更新骑手信息(需登录本人,或 admin/store)
|
||
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: '无权修改该骑手信息' });
|
||
}
|
||
// 不允许通过 PUT 修改密码和 role(仅 admin 可改 role)
|
||
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: "服务器内部错误" });
|
||
}
|
||
});
|
||
|
||
// 获取骑手的订单(需登录本人,或 admin/store)
|
||
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;
|