fix: 修复骑手端缺失接口 + 路由顺序 + nginx rewrite路径映射
1. riders.js: 添加缺失接口 /stats, /orders/available, /orders/:id/accept/complete/cancel - 固定路由放在 /:id 参数路由之前(避免被误匹配) 2. Order.js: 添加 rider 字段 3. nginx rewrite: 修正 store-api 和 rider-api 的路径映射规则
This commit is contained in:
parent
9477a9c199
commit
9e811e683d
|
|
@ -35,6 +35,9 @@ const orderSchema = new mongoose.Schema({
|
|||
// 门店关联
|
||||
storeId: { type: String, index: true }, // 所属门店
|
||||
|
||||
// 骑手关联
|
||||
rider: { type: mongoose.Schema.Types.ObjectId, ref: 'Rider', index: true }, // 接单骑手
|
||||
|
||||
// 合同信息
|
||||
contractUrl: { type: String }, // 合同文件路径
|
||||
contractSigned: { type: Boolean, default: false }, // 合同是否签署
|
||||
|
|
|
|||
|
|
@ -9,35 +9,32 @@ 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,
|
||||
max: 100,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: { success: false, message: '登录尝试过于频繁,请15分钟后再试' }
|
||||
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() : '';
|
||||
|
||||
// 用 select(false) 主动拉取 password 字段
|
||||
const rider = await Rider.findOne({ phone: trimmedPhone }).select('+password');
|
||||
if (!rider) {
|
||||
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
||||
}
|
||||
|
||||
// bcrypt 比对密码
|
||||
const isMatch = await comparePassword(trimmedPassword, 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,
|
||||
|
|
@ -64,7 +61,111 @@ router.post('/login', loginLimiter, validate(schemas.login), async (req, res) =>
|
|||
}
|
||||
});
|
||||
|
||||
// 获取骑手信息(需登录,且只能查看自己;admin/store 可查看任意骑手)
|
||||
// ===== 固定路由(必须放在 /: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('/:id', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const isSelf = req.params.id === req.user.id;
|
||||
|
|
@ -82,7 +183,7 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// 更新骑手信息(需登录本人,或 admin/store)
|
||||
// 更新骑手信息
|
||||
router.put('/:id', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const isSelf = req.params.id === req.user.id;
|
||||
|
|
@ -90,17 +191,12 @@ router.put('/:id', authMiddleware, async (req, res) => {
|
|||
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 }
|
||||
);
|
||||
const rider = await Rider.findByIdAndUpdate(req.params.id, updateData, { new: true, runValidators: true });
|
||||
if (!rider) {
|
||||
return res.status(404).json({ success: false, message: '骑手不存在' });
|
||||
}
|
||||
|
|
@ -110,7 +206,7 @@ router.put('/:id', authMiddleware, async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// 获取骑手的订单(需登录本人,或 admin/store)
|
||||
// 获取骑手的订单
|
||||
router.get('/:id/orders', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const isSelf = req.params.id === req.user.id;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const { comparePassword } = require('../utils/password');
|
|||
// 登录限流
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 10,
|
||||
max: 100,
|
||||
message: { success: false, message: '登录尝试过于频繁' }
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue