安全增强:JWT密钥重置、RBAC五表权限体系、标准用户角色权限分离、门店数据隔离修复
This commit is contained in:
parent
c48e2c2961
commit
e61281ec76
|
|
@ -209,9 +209,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "5.0.4",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
"resolved": "http://mirrors.tencentyun.com/npm/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -1365,15 +1365,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
"resolved": "http://mirrors.tencentyun.com/npm/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
|
||||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
"integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "http://mirrors.tencentyun.com/npm/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,18 @@ const rateLimit = require('express-rate-limit');
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// ─── 自定义中间件 ────────────────────────────────────────────
|
||||||
|
const { authMiddleware } = require('./middleware/auth');
|
||||||
|
const roleAuth = require('./middleware/roleAuth');
|
||||||
|
const rbacAuth = require('./middleware/rbacAuth');
|
||||||
|
|
||||||
|
// ─── 加载模型(注册到 mongoose)───────────────────────────────
|
||||||
|
require('./models/User');
|
||||||
|
require('./models/Role');
|
||||||
|
require('./models/UserRole');
|
||||||
|
require('./models/Permission');
|
||||||
|
require('./models/RolePerm');
|
||||||
|
|
||||||
// ─── 安全中间件 ──────────────────────────────────────────────
|
// ─── 安全中间件 ──────────────────────────────────────────────
|
||||||
|
|
||||||
// helmet: 安全响应头
|
// helmet: 安全响应头
|
||||||
|
|
@ -50,20 +62,33 @@ mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter
|
||||||
.then(() => console.log('✅ MongoDB 连接成功'))
|
.then(() => console.log('✅ MongoDB 连接成功'))
|
||||||
.catch(err => console.error('❌ MongoDB 连接失败:', err.message));
|
.catch(err => console.error('❌ MongoDB 连接失败:', err.message));
|
||||||
|
|
||||||
// ─── 路由 ───────────────────────────────────────────────────
|
// ─── 路由(统一加 authMiddleware,按角色分级授权) ────────────
|
||||||
app.use('/api/vehicles', require('./routes/vehicles'));
|
//
|
||||||
app.use('/api/orders', require('./routes/orders'));
|
/// 管理后台 API(admin only)
|
||||||
app.use('/api/customers', require('./routes/customers'));
|
app.use('/api/vehicles', rbacAuth('vehicles:read', 'vehicles:write'), require('./routes/vehicles'));
|
||||||
app.use('/api/finance', require('./routes/finance'));
|
app.use('/api/customers', rbacAuth('customers:read', 'customers:write'), require('./routes/customers'));
|
||||||
app.use('/api/stores', require('./routes/stores'));
|
app.use('/api/finance', rbacAuth('finance:read'), require('./routes/finance'));
|
||||||
app.use('/api/complaints', require('./routes/complaints'));
|
app.use('/api/orders', rbacAuth('orders:read', 'orders:write'), require('./routes/orders'));
|
||||||
app.use('/api/approvals', require('./routes/approvals'));
|
app.use('/api/approvals', rbacAuth('approvals:read', 'approvals:write'), require('./routes/approvals'));
|
||||||
app.use('/api/payments', require('./routes/payments'));
|
app.use('/api/disputes', rbacAuth('disputes:read', 'disputes:write'), require('./routes/disputes'));
|
||||||
app.use('/api/conflicts', require('./routes/conflicts'));
|
app.use('/api/conflicts', rbacAuth('disputes:read', 'disputes:write'), require('./routes/conflicts'));
|
||||||
app.use('/api/applications', require('./routes/applications'));
|
|
||||||
app.use('/api/disputes', require('./routes/disputes'));
|
/// 门店端 API(store + admin)
|
||||||
|
app.use('/api/stores', rbacAuth('store:read', 'store:write'), require('./routes/stores'));
|
||||||
|
app.use('/api/complaints', rbacAuth('complaints:read', 'complaints:write'), require('./routes/complaints'));
|
||||||
|
app.use('/api/payments', rbacAuth('payments:read', 'payments:write'), require('./routes/payments'));
|
||||||
|
app.use('/api/applications', rbacAuth('applications:read', 'applications:write'), require('./routes/applications'));
|
||||||
|
|
||||||
|
// ─── 独立账号体系登录路由(无需鉴权) ─────────────────────────
|
||||||
|
app.use('/api/admin', require('./routes/adminAuth'));
|
||||||
|
app.use('/api/store-auth', require('./routes/storeAuth'));
|
||||||
|
// ─── 新的 RBAC 统一认证登录路由 ─────────────────────────────
|
||||||
|
app.use('/api/auth', require('./routes/auth'));
|
||||||
|
|
||||||
|
/// 骑手端 API
|
||||||
|
/// 注:riders.js 内部对非登录路由独立加 authMiddleware,故此处不挂载 roleAuth
|
||||||
app.use('/api/riders', require('./routes/riders'));
|
app.use('/api/riders', require('./routes/riders'));
|
||||||
app.use('/api/vehicle-types', require('./routes/vehicleTypes'));
|
app.use('/api/vehicle-types', rbacAuth('vehicleTypes:read', 'vehicleTypes:write'), require('./routes/vehicleTypes'));
|
||||||
|
|
||||||
// ─── 健康检查(不限流) ─────────────────────────────────────
|
// ─── 健康检查(不限流) ─────────────────────────────────────
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* 初始化默认 admin 账号
|
||||||
|
* 用法: node server/initAdmin.js
|
||||||
|
*/
|
||||||
|
require('dotenv').config();
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Admin = require('./models/Admin');
|
||||||
|
const { hashPassword } = require('./utils/password');
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter-rental');
|
||||||
|
|
||||||
|
const existing = await Admin.findOne({ username: 'admin' });
|
||||||
|
if (existing) {
|
||||||
|
console.log('⚠️ Admin already exists:', existing.username);
|
||||||
|
} else {
|
||||||
|
const hashed = await hashPassword('admin123');
|
||||||
|
await Admin.create({
|
||||||
|
username: 'admin',
|
||||||
|
password: hashed,
|
||||||
|
name: '系统管理员',
|
||||||
|
role: 'superadmin'
|
||||||
|
});
|
||||||
|
console.log('✅ Admin created: admin / admin123');
|
||||||
|
}
|
||||||
|
|
||||||
|
await mongoose.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
init().catch(err => { console.error(err); process.exit(1); });
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const User = require('./models/User');
|
||||||
|
const Role = require('./models/Role');
|
||||||
|
const UserRole = require('./models/UserRole');
|
||||||
|
const Permission = require('./models/Permission');
|
||||||
|
const RolePerm = require('./models/RolePerm');
|
||||||
|
const { hashPassword } = require('./utils/password');
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter-rental');
|
||||||
|
console.log('📦 MongoDB 连接成功,开始初始化 RBAC...');
|
||||||
|
|
||||||
|
// 清理旧数据(可选,生产环境请注释掉)
|
||||||
|
await RolePerm.deleteMany({});
|
||||||
|
await Permission.deleteMany({});
|
||||||
|
await UserRole.deleteMany({});
|
||||||
|
await Role.deleteMany({});
|
||||||
|
await User.deleteMany({});
|
||||||
|
|
||||||
|
// 1. 创建角色
|
||||||
|
const roles = {
|
||||||
|
admin: await Role.create({ roleName: 'admin', roleLabel: '管理员', description: '系统管理员' }),
|
||||||
|
store: await Role.create({ roleName: 'store', roleLabel: '商家', description: '门店管理员' }),
|
||||||
|
rider: await Role.create({ roleName: 'rider', roleLabel: '骑手', description: '骑手用户' })
|
||||||
|
};
|
||||||
|
console.log('✅ 角色创建完成');
|
||||||
|
|
||||||
|
// 2. 创建权限
|
||||||
|
const perms = {
|
||||||
|
vehiclesRead: await Permission.create({ permName: 'vehicles:read', permLabel: '查看车辆', module: 'vehicles', action: 'read' }),
|
||||||
|
vehiclesWrite: await Permission.create({ permName: 'vehicles:write', permLabel: '管理车辆', module: 'vehicles', action: 'write' }),
|
||||||
|
ordersRead: await Permission.create({ permName: 'orders:read', permLabel: '查看订单', module: 'orders', action: 'read' }),
|
||||||
|
ordersWrite: await Permission.create({ permName: 'orders:write', permLabel: '管理订单', module: 'orders', action: 'write' }),
|
||||||
|
financeRead: await Permission.create({ permName: 'finance:read', permLabel: '查看财务', module: 'finance', action: 'read' }),
|
||||||
|
usersRead: await Permission.create({ permName: 'users:read', permLabel: '查看用户', module: 'users', action: 'read' }),
|
||||||
|
usersWrite: await Permission.create({ permName: 'users:write', permLabel: '管理用户', module: 'users', action: 'write' }),
|
||||||
|
storeRead: await Permission.create({ permName: 'store:read', permLabel: '查看门店', module: 'store', action: 'read' }),
|
||||||
|
storeWrite: await Permission.create({ permName: 'store:write', permLabel: '管理门店', module: 'store', action: 'write' }),
|
||||||
|
customersRead: await Permission.create({ permName: 'customers:read', permLabel: '查看客户', module: 'customers', action: 'read' }),
|
||||||
|
customersWrite: await Permission.create({ permName: 'customers:write', permLabel: '管理客户', module: 'customers', action: 'write' }),
|
||||||
|
applicationsRead: await Permission.create({ permName: 'applications:read', permLabel: '查看申请', module: 'applications', action: 'read' }),
|
||||||
|
applicationsWrite: await Permission.create({ permName: 'applications:write', permLabel: '管理申请', module: 'applications', action: 'write' }),
|
||||||
|
complaintsRead: await Permission.create({ permName: 'complaints:read', permLabel: '查看投诉', module: 'complaints', action: 'read' }),
|
||||||
|
complaintsWrite: await Permission.create({ permName: 'complaints:write', permLabel: '管理投诉', module: 'complaints', action: 'write' }),
|
||||||
|
disputesRead: await Permission.create({ permName: 'disputes:read', permLabel: '查看纠纷', module: 'disputes', action: 'read' }),
|
||||||
|
disputesWrite: await Permission.create({ permName: 'disputes:write', permLabel: '管理纠纷', module: 'disputes', action: 'write' }),
|
||||||
|
approvalsRead: await Permission.create({ permName: 'approvals:read', permLabel: '查看审批', module: 'approvals', action: 'read' }),
|
||||||
|
approvalsWrite: await Permission.create({ permName: 'approvals:write', permLabel: '管理审批', module: 'approvals', action: 'write' }),
|
||||||
|
paymentsRead: await Permission.create({ permName: 'payments:read', permLabel: '查看支付', module: 'payments', action: 'read' }),
|
||||||
|
paymentsWrite: await Permission.create({ permName: 'payments:write', permLabel: '管理支付', module: 'payments', action: 'write' }),
|
||||||
|
vehicleTypesRead: await Permission.create({ permName: 'vehicleTypes:read', permLabel: '查看车型', module: 'vehicleTypes', action: 'read' }),
|
||||||
|
vehicleTypesWrite: await Permission.create({ permName: 'vehicleTypes:write', permLabel: '管理车型', module: 'vehicleTypes', action: 'write' }),
|
||||||
|
};
|
||||||
|
console.log('✅ 权限创建完成');
|
||||||
|
|
||||||
|
// 3. 角色-权限关联
|
||||||
|
|
||||||
|
// admin: 所有权限
|
||||||
|
for (const key of Object.keys(perms)) {
|
||||||
|
await RolePerm.create({ role: roles.admin._id, permission: perms[key]._id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// store: 门店 + 订单 + 车辆 + 客户 + 投诉 + 申请 + 支付 + 车型(部分写权限)
|
||||||
|
const storePerms = [
|
||||||
|
'storeRead', 'storeWrite',
|
||||||
|
'ordersRead', 'ordersWrite',
|
||||||
|
'vehiclesRead', 'vehiclesWrite',
|
||||||
|
'customersRead', 'customersWrite',
|
||||||
|
'complaintsRead', 'complaintsWrite',
|
||||||
|
'applicationsRead', 'applicationsWrite',
|
||||||
|
'paymentsRead', 'paymentsWrite',
|
||||||
|
'disputesRead',
|
||||||
|
'vehicleTypesRead'
|
||||||
|
];
|
||||||
|
for (const key of storePerms) {
|
||||||
|
await RolePerm.create({ role: roles.store._id, permission: perms[key]._id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// rider: 只读部分
|
||||||
|
const riderPerms = [
|
||||||
|
'ordersRead',
|
||||||
|
'vehiclesRead',
|
||||||
|
'customersRead',
|
||||||
|
'vehicleTypesRead'
|
||||||
|
];
|
||||||
|
for (const key of riderPerms) {
|
||||||
|
await RolePerm.create({ role: roles.rider._id, permission: perms[key]._id });
|
||||||
|
}
|
||||||
|
console.log('✅ 角色-权限关联完成');
|
||||||
|
|
||||||
|
// 4. 创建默认 admin 账号
|
||||||
|
const hashed = await hashPassword('admin123');
|
||||||
|
const adminUser = await User.create({
|
||||||
|
username: 'admin',
|
||||||
|
password: hashed,
|
||||||
|
name: '系统管理员',
|
||||||
|
type: 'admin',
|
||||||
|
status: 'active'
|
||||||
|
});
|
||||||
|
await UserRole.create({ user: adminUser._id, role: roles.admin._id });
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
console.log('═══════════════════════════════════════');
|
||||||
|
console.log('✅ RBAC 初始化完成!');
|
||||||
|
console.log('默认账号: admin / admin123');
|
||||||
|
console.log('═══════════════════════════════════════');
|
||||||
|
|
||||||
|
await mongoose.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
init().catch(err => {
|
||||||
|
console.error('❌ RBAC 初始化失败:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* 初始化默认 store(门店)账号
|
||||||
|
* 用法: node server/initStore.js
|
||||||
|
* 注意:需要先有 Store 记录才能创建门店账号
|
||||||
|
*/
|
||||||
|
require('dotenv').config();
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Store = require('./models/Store');
|
||||||
|
const StoreAuth = require('./models/StoreAuth');
|
||||||
|
const { hashPassword } = require('./utils/password');
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter-rental');
|
||||||
|
|
||||||
|
// 查找第一个已审批的门店
|
||||||
|
const store = await Store.findOne({ approvalStatus: '已通过' });
|
||||||
|
if (!store) {
|
||||||
|
console.log('⚠️ 没有找到已审批的门店,请先创建门店记录');
|
||||||
|
await mongoose.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await StoreAuth.findOne({ storeId: store.storeId });
|
||||||
|
if (existing) {
|
||||||
|
console.log('⚠️ StoreAuth already exists for store:', store.name, '/ storeId:', store.storeId);
|
||||||
|
} else {
|
||||||
|
const hashed = await hashPassword('store123');
|
||||||
|
await StoreAuth.create({
|
||||||
|
storeId: store.storeId,
|
||||||
|
username: store.storeId, // 默认用户名 = 门店编号
|
||||||
|
password: hashed,
|
||||||
|
name: store.manager || '门店管理员',
|
||||||
|
status: 'active'
|
||||||
|
});
|
||||||
|
console.log('✅ StoreAuth created for:', store.name);
|
||||||
|
console.log(' username:', store.storeId, '/ password: store123');
|
||||||
|
}
|
||||||
|
|
||||||
|
await mongoose.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
init().catch(err => { console.error(err); process.exit(1); });
|
||||||
|
|
@ -1,5 +1,16 @@
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT Token 黑名单(已撤销的 jti 集合)
|
||||||
|
*/
|
||||||
|
const revokedTokens = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销指定 jti 的 token
|
||||||
|
*/
|
||||||
|
const revokeToken = (jti) => revokedTokens.add(jti);
|
||||||
|
module.exports.revokeToken = revokeToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT 鉴权中间件
|
* JWT 鉴权中间件
|
||||||
* 验证请求头中的 Bearer token,写入 req.user
|
* 验证请求头中的 Bearer token,写入 req.user
|
||||||
|
|
@ -11,6 +22,10 @@ const authMiddleware = (req, res, next) => {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
// 检查 token 是否已撤销
|
||||||
|
if (decoded.jti && revokedTokens.has(decoded.jti)) {
|
||||||
|
return res.status(401).json({ success: false, message: 'token已失效' });
|
||||||
|
}
|
||||||
req.user = decoded;
|
req.user = decoded;
|
||||||
next();
|
next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ const errorHandler = (err, req, res, next) => {
|
||||||
if (err.name === 'MongoError' || err.name === 'MongooseError') {
|
if (err.name === 'MongoError' || err.name === 'MongooseError') {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: '数据库错误',
|
message: '数据库错误'
|
||||||
error: err.message
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,8 +14,7 @@ const errorHandler = (err, req, res, next) => {
|
||||||
if (err.name === 'ValidationError') {
|
if (err.name === 'ValidationError') {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: '数据验证失败',
|
message: '数据验证失败'
|
||||||
error: err.message
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,16 +22,14 @@ const errorHandler = (err, req, res, next) => {
|
||||||
if (err.name === 'CastError') {
|
if (err.name === 'CastError') {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: '数据不存在',
|
message: '数据不存在'
|
||||||
error: err.message
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认错误
|
// 默认错误
|
||||||
res.status(err.status || 500).json({
|
res.status(err.status || 500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: err.message || '服务器内部错误',
|
message: '服务器内部错误'
|
||||||
error: process.env.NODE_ENV === 'development' ? err.stack : undefined
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
const revokedTokens = new Set();
|
||||||
|
|
||||||
|
const revokeToken = (jti) => revokedTokens.add(jti);
|
||||||
|
module.exports.revokeToken = revokeToken;
|
||||||
|
|
||||||
|
const rbacAuth = (...requiredPerms) => {
|
||||||
|
return (req, res, next) => {
|
||||||
|
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||||
|
if (!token) {
|
||||||
|
return res.status(401).json({ success: false, message: '未登录' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
|
||||||
|
if (decoded.jti && revokedTokens.has(decoded.jti)) {
|
||||||
|
return res.status(401).json({ success: false, message: 'token已失效' });
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = decoded;
|
||||||
|
|
||||||
|
if (requiredPerms.length > 0) {
|
||||||
|
const userPerms = decoded.permissions || [];
|
||||||
|
const hasPerm = requiredPerms.some(perm => userPerms.includes(perm));
|
||||||
|
if (!hasPerm) {
|
||||||
|
return res.status(403).json({ success: false, message: '权限不足' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(401).json({ success: false, message: 'token无效或已过期' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = rbacAuth;
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* 角色权限分级中间件(RBAC)
|
||||||
|
* 用法: roleAuth('admin'), roleAuth('admin', 'store', 'rider')
|
||||||
|
*
|
||||||
|
* 角色说明:
|
||||||
|
* admin - 管理后台最高权限,可访问所有 API
|
||||||
|
* store - 门店管理员,可访问门店相关数据和业务操作
|
||||||
|
* rider - 骑手,只能访问自己的数据和公开浏览接口
|
||||||
|
*/
|
||||||
|
const roleAuth = (...allowedRoles) => {
|
||||||
|
return (req, res, next) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(401).json({ success: false, message: '未登录' });
|
||||||
|
}
|
||||||
|
const { role } = req.user;
|
||||||
|
if (!allowedRoles.includes(role)) {
|
||||||
|
return res.status(403).json({ success: false, message: '无权访问' });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = roleAuth;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const AdminSchema = new mongoose.Schema({
|
||||||
|
username: { type: String, required: true, unique: true }, // 登录名,如 "admin"
|
||||||
|
password: { type: String, required: true, select: false }, // bcrypt 加密
|
||||||
|
name: { type: String, required: true },
|
||||||
|
role: { type: String, enum: ['admin', 'superadmin'], default: 'admin' },
|
||||||
|
status: { type: String, enum: ['active', 'disabled'], default: 'active' }
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('Admin', AdminSchema);
|
||||||
|
|
@ -32,6 +32,9 @@ const orderSchema = new mongoose.Schema({
|
||||||
paymentMethod: { type: String, enum: ['微信', '支付宝', '现金', '银行卡'] },
|
paymentMethod: { type: String, enum: ['微信', '支付宝', '现金', '银行卡'] },
|
||||||
paymentDate: { type: Date },
|
paymentDate: { type: Date },
|
||||||
|
|
||||||
|
// 门店关联
|
||||||
|
storeId: { type: String, index: true }, // 所属门店
|
||||||
|
|
||||||
// 合同信息
|
// 合同信息
|
||||||
contractUrl: { type: String }, // 合同文件路径
|
contractUrl: { type: String }, // 合同文件路径
|
||||||
contractSigned: { type: Boolean, default: false }, // 合同是否签署
|
contractSigned: { type: Boolean, default: false }, // 合同是否签署
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const PermissionSchema = new mongoose.Schema({
|
||||||
|
permName: { type: String, required: true, unique: true },
|
||||||
|
permLabel: { type: String, required: true },
|
||||||
|
module: { type: String, required: true },
|
||||||
|
action: { type: String, required: true }
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
// 索引
|
||||||
|
PermissionSchema.index({ permName: 1 }, { unique: true });
|
||||||
|
PermissionSchema.index({ module: 1 });
|
||||||
|
PermissionSchema.index({ action: 1 });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('Permission', PermissionSchema);
|
||||||
|
|
@ -9,8 +9,8 @@ const riderSchema = new mongoose.Schema({
|
||||||
// 密码(bcrypt 哈希存储,查询时默认不返回)
|
// 密码(bcrypt 哈希存储,查询时默认不返回)
|
||||||
password: { type: String, required: true, select: false },
|
password: { type: String, required: true, select: false },
|
||||||
|
|
||||||
// 角色(customer=普通用户, admin=管理员, store=门店管理员)
|
// 角色(customer=普通用户, admin=管理员, store=门店管理员, rider=骑手)
|
||||||
role: { type: String, enum: ['customer', 'admin', 'store'], default: 'customer' },
|
role: { type: String, enum: ['customer', 'admin', 'store', 'rider'], default: 'customer' },
|
||||||
|
|
||||||
// 接单状态
|
// 接单状态
|
||||||
status: {
|
status: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const RoleSchema = new mongoose.Schema({
|
||||||
|
roleName: { type: String, required: true, unique: true },
|
||||||
|
roleLabel: { type: String, required: true },
|
||||||
|
description: { type: String }
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('Role', RoleSchema);
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const RolePermSchema = new mongoose.Schema({
|
||||||
|
role: { type: mongoose.Schema.Types.ObjectId, ref: 'Role', required: true },
|
||||||
|
permission: { type: mongoose.Schema.Types.ObjectId, ref: 'Permission', required: true }
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
// 索引
|
||||||
|
RolePermSchema.index({ role: 1, permission: 1 }, { unique: true });
|
||||||
|
RolePermSchema.index({ role: 1 });
|
||||||
|
RolePermSchema.index({ permission: 1 });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('RolePerm', RolePermSchema);
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const StoreAuthSchema = new mongoose.Schema({
|
||||||
|
storeId: { type: String, required: true, unique: true }, // 关联的门店编号
|
||||||
|
username: { type: String, required: true, unique: true }, // 门店登录用户名
|
||||||
|
password: { type: String, required: true, select: false }, // bcrypt 加密
|
||||||
|
name: { type: String, required: true }, // 门店负责人姓名
|
||||||
|
status: { type: String, enum: ['active', 'disabled'], default: 'active' }
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('StoreAuth', StoreAuthSchema);
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const UserSchema = new mongoose.Schema({
|
||||||
|
username: { type: String, required: true, unique: true },
|
||||||
|
password: { type: String, required: true, select: false },
|
||||||
|
phone: { type: String },
|
||||||
|
name: { type: String, required: true },
|
||||||
|
status: { type: String, enum: ['active', 'disabled'], default: 'active' },
|
||||||
|
type: { type: String, enum: ['admin', 'store', 'rider'], required: true },
|
||||||
|
storeId: { type: String } // store 类型账号关联的门店ID
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
// 索引
|
||||||
|
UserSchema.index({ username: 1 }, { unique: true });
|
||||||
|
UserSchema.index({ phone: 1 });
|
||||||
|
UserSchema.index({ type: 1 });
|
||||||
|
UserSchema.index({ status: 1 });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('User', UserSchema);
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const UserRoleSchema = new mongoose.Schema({
|
||||||
|
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
|
||||||
|
role: { type: mongoose.Schema.Types.ObjectId, ref: 'Role', required: true }
|
||||||
|
}, { timestamps: true });
|
||||||
|
|
||||||
|
UserRoleSchema.index({ user: 1, role: 1 }, { unique: true });
|
||||||
|
|
||||||
|
module.exports = mongoose.model('UserRole', UserRoleSchema);
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
const Admin = require('../models/Admin');
|
||||||
|
const { comparePassword } = require('../utils/password');
|
||||||
|
|
||||||
|
// 登录限流
|
||||||
|
const loginLimiter = rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000,
|
||||||
|
max: 10,
|
||||||
|
message: { success: false, message: '登录尝试过于频繁' }
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/login', loginLimiter, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
if (!username || !password) {
|
||||||
|
return res.status(400).json({ success: false, message: '用户名和密码不能为空' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const admin = await Admin.findOne({ username }).select('+password');
|
||||||
|
if (!admin || admin.status !== 'active') {
|
||||||
|
return res.status(401).json({ success: false, message: '用户名或密码错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMatch = await comparePassword(password, admin.password);
|
||||||
|
if (!isMatch) {
|
||||||
|
return res.status(401).json({ success: false, message: '用户名或密码错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ id: admin._id, role: admin.role, type: 'admin', jti: Math.random().toString(36) },
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { id: admin._id, username: admin.username, name: admin.name, role: admin.role, token }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
@ -1,41 +1,46 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Application = require('../models/Application');
|
const Application = require('../models/Application');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
router.get('/', async (req, res) => {
|
// 获取所有申请(admin 或 store)
|
||||||
|
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const apps = await Application.find().populate('store');
|
const apps = await Application.find().populate('store');
|
||||||
res.json({ success: true, data: apps });
|
res.json({ success: true, data: apps });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/', async (req, res) => {
|
// 创建申请(admin 或 store)
|
||||||
|
router.post('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const app = new Application(req.body);
|
const app = new Application(req.body);
|
||||||
await app.save();
|
await app.save();
|
||||||
res.json({ success: true, data: app });
|
res.json({ success: true, data: app });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/:id', async (req, res) => {
|
// 更新申请(admin 或 store)
|
||||||
|
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const app = await Application.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const app = await Application.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: app });
|
res.json({ success: true, data: app });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:id', async (req, res) => {
|
// 删除申请(admin 或 store)
|
||||||
|
router.delete('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Application.findByIdAndDelete(req.params.id);
|
await Application.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,46 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Approval = require('../models/Approval');
|
const Approval = require('../models/Approval');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
// 获取所有审批
|
// 获取所有审批(仅 admin)
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const approvals = await Approval.find();
|
const approvals = await Approval.find();
|
||||||
res.json({ success: true, data: approvals });
|
res.json({ success: true, data: approvals });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建审批
|
// 创建审批(仅 admin)
|
||||||
router.post('/', async (req, res) => {
|
router.post('/', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const approval = new Approval(req.body);
|
const approval = new Approval(req.body);
|
||||||
await approval.save();
|
await approval.save();
|
||||||
res.json({ success: true, data: approval });
|
res.json({ success: true, data: approval });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新审批
|
// 更新审批(仅 admin)
|
||||||
router.put('/:id', async (req, res) => {
|
router.put('/:id', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const approval = await Approval.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const approval = await Approval.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: approval });
|
res.json({ success: true, data: approval });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 删除审批
|
// 删除审批(仅 admin)
|
||||||
router.delete('/:id', async (req, res) => {
|
router.delete('/:id', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Approval.findByIdAndDelete(req.params.id);
|
await Approval.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
const User = require('../models/User');
|
||||||
|
const UserRole = require('../models/UserRole');
|
||||||
|
const RolePerm = require('../models/RolePerm');
|
||||||
|
const { comparePassword } = require('../utils/password');
|
||||||
|
|
||||||
|
const loginLimiter = rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000,
|
||||||
|
max: 10,
|
||||||
|
message: { success: false, message: '登录尝试过于频繁' }
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/login', loginLimiter, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
return res.status(400).json({ success: false, message: '用户名和密码不能为空' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findOne({ username }).select('+password');
|
||||||
|
if (!user || user.status !== 'active') {
|
||||||
|
return res.status(401).json({ success: false, message: '用户名或密码错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMatch = await comparePassword(password, user.password);
|
||||||
|
if (!isMatch) {
|
||||||
|
return res.status(401).json({ success: false, message: '用户名或密码错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const userRole = await UserRole.findOne({ user: user._id }).populate('role');
|
||||||
|
if (!userRole) {
|
||||||
|
return res.status(403).json({ success: false, message: '该用户未分配角色' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const rolePerms = await RolePerm.find({ role: userRole.role._id }).populate('permission');
|
||||||
|
const permissions = rolePerms.map(rp => rp.permission.permName);
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{
|
||||||
|
id: user._id,
|
||||||
|
username: user.username,
|
||||||
|
type: user.type,
|
||||||
|
role: userRole.role.roleName,
|
||||||
|
permissions,
|
||||||
|
jti: Math.random().toString(36)
|
||||||
|
},
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
id: user._id,
|
||||||
|
username: user.username,
|
||||||
|
name: user.name,
|
||||||
|
type: user.type,
|
||||||
|
role: userRole.role.roleName,
|
||||||
|
roleLabel: userRole.role.roleLabel,
|
||||||
|
permissions,
|
||||||
|
token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Login error:', error);
|
||||||
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
@ -1,45 +1,46 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Complaint = require('../models/Complaint');
|
const Complaint = require('../models/Complaint');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
// 获取所有投诉
|
// 获取所有投诉(admin 或 store)
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const complaints = await Complaint.find().populate('customer order');
|
const complaints = await Complaint.find().populate('customer order');
|
||||||
res.json({ success: true, data: complaints });
|
res.json({ success: true, data: complaints });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建投诉
|
// 创建投诉(admin 或 store)
|
||||||
router.post('/', async (req, res) => {
|
router.post('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const complaint = new Complaint(req.body);
|
const complaint = new Complaint(req.body);
|
||||||
await complaint.save();
|
await complaint.save();
|
||||||
res.json({ success: true, data: complaint });
|
res.json({ success: true, data: complaint });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新投诉
|
// 更新投诉(admin 或 store)
|
||||||
router.put('/:id', async (req, res) => {
|
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const complaint = await Complaint.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const complaint = await Complaint.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: complaint });
|
res.json({ success: true, data: complaint });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 删除投诉
|
// 删除投诉(admin 或 store)
|
||||||
router.delete('/:id', async (req, res) => {
|
router.delete('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Complaint.findByIdAndDelete(req.params.id);
|
await Complaint.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,46 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Conflict = require('../models/Conflict');
|
const Conflict = require('../models/Conflict');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
// 获取所有矛盾记录
|
// 获取所有矛盾记录(admin 或 store)
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const conflicts = await Conflict.find();
|
const conflicts = await Conflict.find();
|
||||||
res.json({ success: true, data: conflicts });
|
res.json({ success: true, data: conflicts });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建矛盾记录
|
// 创建矛盾记录(admin 或 store)
|
||||||
router.post('/', async (req, res) => {
|
router.post('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const conflict = new Conflict(req.body);
|
const conflict = new Conflict(req.body);
|
||||||
await conflict.save();
|
await conflict.save();
|
||||||
res.json({ success: true, data: conflict });
|
res.json({ success: true, data: conflict });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新矛盾记录
|
// 更新矛盾记录(admin 或 store)
|
||||||
router.put('/:id', async (req, res) => {
|
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const conflict = await Conflict.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const conflict = await Conflict.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: conflict });
|
res.json({ success: true, data: conflict });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 删除矛盾记录
|
// 删除矛盾记录(admin 或 store)
|
||||||
router.delete('/:id', async (req, res) => {
|
router.delete('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Conflict.findByIdAndDelete(req.params.id);
|
await Conflict.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Customer = require('../models/Customer');
|
const Customer = require('../models/Customer');
|
||||||
|
const Store = require('../models/Store');
|
||||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
const { validate } = require('../middleware/validate');
|
const { validate } = require('../middleware/validate');
|
||||||
const { schemas } = require('../middleware/validate');
|
const { schemas } = require('../middleware/validate');
|
||||||
|
|
@ -11,7 +12,7 @@ router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res)
|
||||||
const customers = await Customer.find();
|
const customers = await Customer.find();
|
||||||
res.json({ success: true, data: customers });
|
res.json({ success: true, data: customers });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -22,7 +23,7 @@ router.get('/:id', authMiddleware, requireRole('admin', 'store'), async (req, re
|
||||||
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
||||||
res.json({ success: true, data: customer });
|
res.json({ success: true, data: customer });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ router.post('/', authMiddleware, requireRole('admin', 'store'), validate(schemas
|
||||||
await customer.save();
|
await customer.save();
|
||||||
res.status(201).json({ success: true, data: customer });
|
res.status(201).json({ success: true, data: customer });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, re
|
||||||
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
||||||
res.json({ success: true, data: customer });
|
res.json({ success: true, data: customer });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@ router.delete('/:id', authMiddleware, requireRole('admin'), async (req, res) =>
|
||||||
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
||||||
res.json({ success: true, message: '客户已删除' });
|
res.json({ success: true, message: '客户已删除' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -72,7 +73,7 @@ router.get('/search/:keyword', authMiddleware, requireRole('admin', 'store'), as
|
||||||
});
|
});
|
||||||
res.json({ success: true, data: customers });
|
res.json({ success: true, data: customers });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -93,7 +94,7 @@ router.patch('/:id/credit', authMiddleware, requireRole('admin', 'store'), async
|
||||||
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
if (!customer) return res.status(404).json({ success: false, message: '客户不存在' });
|
||||||
res.json({ success: true, data: customer });
|
res.json({ success: true, data: customer });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,46 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Dispute = require('../models/Dispute');
|
const Dispute = require('../models/Dispute');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
router.get('/', async (req, res) => {
|
// 获取所有纠纷(admin 或 store)
|
||||||
|
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const disputes = await Dispute.find();
|
const disputes = await Dispute.find();
|
||||||
res.json({ success: true, data: disputes });
|
res.json({ success: true, data: disputes });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/', async (req, res) => {
|
// 创建纠纷(admin 或 store)
|
||||||
|
router.post('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const dispute = new Dispute(req.body);
|
const dispute = new Dispute(req.body);
|
||||||
await dispute.save();
|
await dispute.save();
|
||||||
res.json({ success: true, data: dispute });
|
res.json({ success: true, data: dispute });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/:id', async (req, res) => {
|
// 更新纠纷(admin 或 store)
|
||||||
|
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const dispute = await Dispute.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const dispute = await Dispute.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: dispute });
|
res.json({ success: true, data: dispute });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:id', async (req, res) => {
|
// 删除纠纷(admin 或 store)
|
||||||
|
router.delete('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Dispute.findByIdAndDelete(req.params.id);
|
await Dispute.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ router.get('/', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ router.post('/', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
await payment.save();
|
await payment.save();
|
||||||
res.json({ success: true, data: payment });
|
res.json({ success: true, data: payment });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ router.put('/:id', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
const payment = await Payment.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const payment = await Payment.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: payment });
|
res.json({ success: true, data: payment });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ router.delete('/:id', authMiddleware, requireRole('admin'), async (req, res) =>
|
||||||
await Payment.findByIdAndDelete(req.params.id);
|
await Payment.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ router.post('/delete-all', authMiddleware, requireRole('admin'), async (req, res
|
||||||
await Payment.deleteMany({});
|
await Payment.deleteMany({});
|
||||||
res.json({ success: true, message: 'All payments deleted' });
|
res.json({ success: true, message: 'All payments deleted' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ router.get('/stats', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ const router = express.Router();
|
||||||
const Order = require('../models/Order');
|
const Order = require('../models/Order');
|
||||||
const Vehicle = require('../models/Vehicle');
|
const Vehicle = require('../models/Vehicle');
|
||||||
const Customer = require('../models/Customer');
|
const Customer = require('../models/Customer');
|
||||||
|
const Store = require('../models/Store');
|
||||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
const { validate } = require('../middleware/validate');
|
const { validate } = require('../middleware/validate');
|
||||||
const { schemas } = require('../middleware/validate');
|
const { schemas } = require('../middleware/validate');
|
||||||
|
|
@ -18,15 +19,17 @@ router.get('/', authMiddleware, async (req, res) => {
|
||||||
// 此处通过 rider id 查找(假设订单中 rider 字段关联骑手)
|
// 此处通过 rider id 查找(假设订单中 rider 字段关联骑手)
|
||||||
filter.rider = req.user.id;
|
filter.rider = req.user.id;
|
||||||
} else if (req.user.role === 'store') {
|
} else if (req.user.role === 'store') {
|
||||||
// store 角色可按门店过滤(前端传 storeId)
|
// store 角色永远只能看自己门店的数据,忽略前端传的 storeId 参数
|
||||||
if (req.query.storeId) filter.storeId = req.query.storeId;
|
if (req.user.storeId) {
|
||||||
|
filter.storeId = req.user.storeId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const orders = await Order.find(filter)
|
const orders = await Order.find(filter)
|
||||||
.populate('customer', 'name phone')
|
.populate('customer', 'name phone')
|
||||||
.populate('vehicle', 'model vehicleId');
|
.populate('vehicle', 'model vehicleId');
|
||||||
res.json({ success: true, data: orders });
|
res.json({ success: true, data: orders });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -45,7 +48,7 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
||||||
}
|
}
|
||||||
res.json({ success: true, data: order });
|
res.json({ success: true, data: order });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -122,7 +125,7 @@ router.post('/', authMiddleware, requireRole('admin', 'store'), validate(schemas
|
||||||
|
|
||||||
res.status(201).json({ success: true, data: order });
|
res.status(201).json({ success: true, data: order });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -133,7 +136,7 @@ router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, re
|
||||||
if (!order) return res.status(404).json({ success: false, message: '订单不存在' });
|
if (!order) return res.status(404).json({ success: false, message: '订单不存在' });
|
||||||
res.json({ success: true, data: order });
|
res.json({ success: true, data: order });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -164,7 +167,7 @@ router.patch('/:id/complete', authMiddleware, requireRole('admin', 'store'), asy
|
||||||
|
|
||||||
res.json({ success: true, data: order });
|
res.json({ success: true, data: order });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -197,7 +200,7 @@ router.patch('/:id/cancel', authMiddleware, requireRole('admin', 'store'), async
|
||||||
|
|
||||||
res.json({ success: true, data: order });
|
res.json({ success: true, data: order });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -221,7 +224,7 @@ router.get('/status/overdue', authMiddleware, requireRole('admin', 'store'), asy
|
||||||
}
|
}
|
||||||
res.json({ success: true, data: orders });
|
res.json({ success: true, data: orders });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -233,7 +236,7 @@ router.get('/status/:status', authMiddleware, requireRole('admin', 'store'), asy
|
||||||
.populate('vehicle', 'model vehicleId');
|
.populate('vehicle', 'model vehicleId');
|
||||||
res.json({ success: true, data: orders });
|
res.json({ success: true, data: orders });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,46 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Payment = require('../models/Payment');
|
const Payment = require('../models/Payment');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
// 获取所有打款记录
|
// 获取所有打款记录(需 admin 或 store)
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const payments = await Payment.find();
|
const payments = await Payment.find();
|
||||||
res.json({ success: true, data: payments });
|
res.json({ success: true, data: payments });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建打款记录
|
// 创建打款记录(需 admin 或 store)
|
||||||
router.post('/', async (req, res) => {
|
router.post('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const payment = new Payment(req.body);
|
const payment = new Payment(req.body);
|
||||||
await payment.save();
|
await payment.save();
|
||||||
res.json({ success: true, data: payment });
|
res.json({ success: true, data: payment });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新打款记录
|
// 更新打款记录(需 admin 或 store)
|
||||||
router.put('/:id', async (req, res) => {
|
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const payment = await Payment.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
const payment = await Payment.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||||
res.json({ success: true, data: payment });
|
res.json({ success: true, data: payment });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 删除打款记录
|
// 删除打款记录(需 admin 或 store)
|
||||||
router.delete('/:id', async (req, res) => {
|
router.delete('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Payment.findByIdAndDelete(req.params.id);
|
await Payment.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const rateLimit = require('express-rate-limit');
|
||||||
const Rider = require('../models/Rider');
|
const Rider = require('../models/Rider');
|
||||||
const Order = require('../models/Order');
|
const Order = require('../models/Order');
|
||||||
const { comparePassword } = require('../utils/password');
|
const { comparePassword } = require('../utils/password');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
const { validate } = require('../middleware/validate');
|
const { validate } = require('../middleware/validate');
|
||||||
const { schemas } = require('../middleware/validate');
|
const { schemas } = require('../middleware/validate');
|
||||||
|
|
||||||
|
|
@ -17,7 +18,7 @@ const loginLimiter = rateLimit({
|
||||||
message: { success: false, message: '登录尝试过于频繁,请15分钟后再试' }
|
message: { success: false, message: '登录尝试过于频繁,请15分钟后再试' }
|
||||||
});
|
});
|
||||||
|
|
||||||
// 骑手登录
|
// 骑手登录(无需鉴权)
|
||||||
router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => {
|
router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { phone, password } = req.body;
|
const { phone, password } = req.body;
|
||||||
|
|
@ -34,9 +35,9 @@ router.post('/login', loginLimiter, validate(schemas.login), async (req, res) =>
|
||||||
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
return res.status(401).json({ success: false, message: '手机号或密码错误' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 签发 JWT(包含 id、role)
|
// 签发 JWT(包含 id、role、jti)
|
||||||
const token = jwt.sign(
|
const token = jwt.sign(
|
||||||
{ id: rider._id, role: rider.role, phone: rider.phone },
|
{ id: rider._id, role: rider.role, type: 'rider', phone: rider.phone, jti: Math.random().toString(36) },
|
||||||
process.env.JWT_SECRET,
|
process.env.JWT_SECRET,
|
||||||
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
|
||||||
);
|
);
|
||||||
|
|
@ -57,31 +58,45 @@ router.post('/login', loginLimiter, validate(schemas.login), async (req, res) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取骑手信息(需登录)
|
// 获取骑手信息(需登录,且只能查看自己;admin/store 可查看任意骑手)
|
||||||
router.get('/:id', async (req, res) => {
|
router.get('/:id', authMiddleware, async (req, res) => {
|
||||||
try {
|
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);
|
const rider = await Rider.findById(req.params.id);
|
||||||
if (!rider) {
|
if (!rider) {
|
||||||
return res.status(404).json({ success: false, message: '骑手不存在' });
|
return res.status(404).json({ success: false, message: '骑手不存在' });
|
||||||
}
|
}
|
||||||
res.json({ success: true, data: rider });
|
res.json({ success: true, data: rider });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新骑手信息(如状态切换,需登录本人或管理员)
|
// 更新骑手信息(需登录本人,或 admin/store)
|
||||||
router.put('/:id', async (req, res) => {
|
router.put('/:id', authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// 不允许通过 PUT 修改密码和 role
|
const isSelf = req.params.id === req.user.id;
|
||||||
const { password, role, ...safeBody } = req.body;
|
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(
|
const rider = await Rider.findByIdAndUpdate(
|
||||||
req.params.id,
|
req.params.id,
|
||||||
safeBody,
|
updateData,
|
||||||
{ new: true, runValidators: true }
|
{ new: true, runValidators: true }
|
||||||
);
|
);
|
||||||
if (!rider) {
|
if (!rider) {
|
||||||
|
|
@ -89,20 +104,25 @@ router.put('/:id', async (req, res) => {
|
||||||
}
|
}
|
||||||
res.json({ success: true, data: rider });
|
res.json({ success: true, data: rider });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取骑手的订单
|
// 获取骑手的订单(需登录本人,或 admin/store)
|
||||||
router.get('/:id/orders', async (req, res) => {
|
router.get('/:id/orders', authMiddleware, async (req, res) => {
|
||||||
try {
|
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 })
|
const orders = await Order.find({ rider: req.params.id })
|
||||||
.populate('customer', 'name phone')
|
.populate('customer', 'name phone')
|
||||||
.populate('vehicle', 'vehicleId model color')
|
.populate('vehicle', 'vehicleId model color')
|
||||||
.sort({ createdAt: -1 });
|
.sort({ createdAt: -1 });
|
||||||
res.json({ success: true, data: orders });
|
res.json({ success: true, data: orders });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
const User = require('../models/User');
|
||||||
|
const UserRole = require('../models/UserRole');
|
||||||
|
const Role = require('../models/Role');
|
||||||
|
const { comparePassword } = require('../utils/password');
|
||||||
|
|
||||||
|
// 登录限流
|
||||||
|
const loginLimiter = rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000,
|
||||||
|
max: 10,
|
||||||
|
message: { success: false, message: '登录尝试过于频繁' }
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/login', loginLimiter, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
if (!username || !password) {
|
||||||
|
return res.status(400).json({ success: false, message: '用户名和密码不能为空' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 User 表查 store 类型账号
|
||||||
|
const user = await User.findOne({ username, type: 'store' }).select('+password');
|
||||||
|
// 查关联的门店
|
||||||
|
const Store = require('../models/Store');
|
||||||
|
const store = await Store.findOne({ storeId: user.storeId });
|
||||||
|
if (!user || user.status !== 'active') {
|
||||||
|
return res.status(401).json({ success: false, message: '用户名或密码错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMatch = await comparePassword(password, user.password);
|
||||||
|
if (!isMatch) {
|
||||||
|
return res.status(401).json({ success: false, message: '用户名或密码错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{
|
||||||
|
id: user._id,
|
||||||
|
role: 'store',
|
||||||
|
type: 'store',
|
||||||
|
storeId: user.storeId || null,
|
||||||
|
permissions: ['store:read', 'store:write', 'orders:read', 'orders:write', 'vehicles:read', 'vehicles:write', 'vehicleTypes:read'],
|
||||||
|
jti: Math.random().toString(36)
|
||||||
|
},
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
id: store ? store._id : user._id, // 门店的 MongoDB _id
|
||||||
|
storeId: user.storeId, // 门店编号如 STORE001
|
||||||
|
username: user.username,
|
||||||
|
name: user.name,
|
||||||
|
role: 'store',
|
||||||
|
token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
@ -8,10 +8,37 @@ const { schemas } = require('../middleware/validate');
|
||||||
// 获取所有门店(登录即可)
|
// 获取所有门店(登录即可)
|
||||||
router.get('/', authMiddleware, async (req, res) => {
|
router.get('/', authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
// store 角色只能看自己关联的门店
|
||||||
|
if (req.user.role === 'store' && req.user.storeId) {
|
||||||
|
const stores = await Store.find({ storeId: req.user.storeId });
|
||||||
|
return res.json({ success: true, data: stores });
|
||||||
|
}
|
||||||
const stores = await Store.find();
|
const stores = await Store.find();
|
||||||
res.json({ success: true, data: stores });
|
res.json({ success: true, data: stores });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取单个门店
|
||||||
|
router.get('/:id', authMiddleware, async (req, res) => {
|
||||||
|
try {
|
||||||
|
let store;
|
||||||
|
// 如果是 MongoDB ObjectId 格式则用 findById,否则用 storeId 字段查
|
||||||
|
if (req.params.id.match(/^[0-9a-fA-F]{24}$/)) {
|
||||||
|
store = await Store.findById(req.params.id);
|
||||||
|
} else {
|
||||||
|
store = await Store.findOne({ storeId: req.params.id });
|
||||||
|
}
|
||||||
|
if (!store) return res.status(404).json({ success: false, message: '门店不存在' });
|
||||||
|
|
||||||
|
// store 角色只能看自己关联的门店
|
||||||
|
if (req.user.role === 'store' && req.user.storeId && store.storeId !== req.user.storeId) {
|
||||||
|
return res.status(403).json({ success: false, message: '无权操作该门店数据' });
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: store });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -22,7 +49,7 @@ router.post('/', authMiddleware, requireRole('admin'), validate(schemas.store),
|
||||||
await store.save();
|
await store.save();
|
||||||
res.json({ success: true, data: store });
|
res.json({ success: true, data: store });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -33,7 +60,7 @@ router.put('/:id', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||||
if (!store) return res.status(404).json({ success: false, message: '门店不存在' });
|
if (!store) return res.status(404).json({ success: false, message: '门店不存在' });
|
||||||
res.json({ success: true, data: store });
|
res.json({ success: true, data: store });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -43,7 +70,7 @@ router.delete('/:id', authMiddleware, requireRole('admin'), async (req, res) =>
|
||||||
await Store.findByIdAndDelete(req.params.id);
|
await Store.findByIdAndDelete(req.params.id);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,33 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const VehicleType = require('../models/VehicleType');
|
const VehicleType = require('../models/VehicleType');
|
||||||
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
|
|
||||||
// 获取所有车型(支持按 storeId 筛选)
|
// 获取所有车型(所有登录用户均可浏览)
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const filter = {};
|
const filter = {};
|
||||||
if (req.query.storeId) filter.storeId = req.query.storeId;
|
if (req.query.storeId) filter.storeId = req.query.storeId;
|
||||||
const vehicleTypes = await VehicleType.find(filter).sort({ createdAt: -1 });
|
const vehicleTypes = await VehicleType.find(filter).sort({ createdAt: -1 });
|
||||||
res.json({ success: true, data: vehicleTypes });
|
res.json({ success: true, data: vehicleTypes });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 创建车型
|
// 创建车型(需 admin 或 store)
|
||||||
router.post('/', async (req, res) => {
|
router.post('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const vehicleType = new VehicleType(req.body);
|
const vehicleType = new VehicleType(req.body);
|
||||||
await vehicleType.save();
|
await vehicleType.save();
|
||||||
res.status(201).json({ success: true, data: vehicleType });
|
res.status(201).json({ success: true, data: vehicleType });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新车型
|
// 更新车型(需 admin 或 store)
|
||||||
router.put('/:id', async (req, res) => {
|
router.put('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const vehicleType = await VehicleType.findByIdAndUpdate(
|
const vehicleType = await VehicleType.findByIdAndUpdate(
|
||||||
req.params.id,
|
req.params.id,
|
||||||
|
|
@ -38,12 +39,12 @@ router.put('/:id', async (req, res) => {
|
||||||
}
|
}
|
||||||
res.json({ success: true, data: vehicleType });
|
res.json({ success: true, data: vehicleType });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 删除车型
|
// 删除车型(需 admin 或 store)
|
||||||
router.delete('/:id', async (req, res) => {
|
router.delete('/:id', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const vehicleType = await VehicleType.findByIdAndDelete(req.params.id);
|
const vehicleType = await VehicleType.findByIdAndDelete(req.params.id);
|
||||||
if (!vehicleType) {
|
if (!vehicleType) {
|
||||||
|
|
@ -51,7 +52,7 @@ router.delete('/:id', async (req, res) => {
|
||||||
}
|
}
|
||||||
res.json({ success: true, message: '车型已删除' });
|
res.json({ success: true, message: '车型已删除' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Vehicle = require('../models/Vehicle');
|
const Vehicle = require('../models/Vehicle');
|
||||||
|
const Store = require('../models/Store');
|
||||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||||
const { validate } = require('../middleware/validate');
|
const { validate } = require('../middleware/validate');
|
||||||
const { schemas } = require('../middleware/validate');
|
const { schemas } = require('../middleware/validate');
|
||||||
|
|
@ -9,12 +10,18 @@ const { schemas } = require('../middleware/validate');
|
||||||
router.get('/', authMiddleware, async (req, res) => {
|
router.get('/', authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const filter = {};
|
const filter = {};
|
||||||
if (req.query.storeId) filter.storeId = req.query.storeId;
|
// store 角色查询时校验 storeId 归属(只能看自己关联的门店)
|
||||||
|
if (req.user.role === 'store' && req.user.storeId) {
|
||||||
|
// store 用户永远只能看自己门店的数据,忽略前端传的 storeId 参数
|
||||||
|
filter.storeId = req.user.storeId;
|
||||||
|
} else if (req.query.storeId) {
|
||||||
|
filter.storeId = req.query.storeId;
|
||||||
|
}
|
||||||
if (req.query.status) filter.status = req.query.status;
|
if (req.query.status) filter.status = req.query.status;
|
||||||
const vehicles = await Vehicle.find(filter).populate('currentOrderId');
|
const vehicles = await Vehicle.find(filter).populate('currentOrderId');
|
||||||
res.json({ success: true, data: vehicles });
|
res.json({ success: true, data: vehicles });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -25,7 +32,7 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
||||||
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
||||||
res.json({ success: true, data: vehicle });
|
res.json({ success: true, data: vehicle });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -38,7 +45,7 @@ router.post('/', authMiddleware, requireRole('admin', 'store'), validate(schemas
|
||||||
await vehicle.save();
|
await vehicle.save();
|
||||||
res.status(201).json({ success: true, data: vehicle });
|
res.status(201).json({ success: true, data: vehicle });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -51,7 +58,7 @@ router.put('/:id', authMiddleware, requireRole('admin', 'store'), validate(schem
|
||||||
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
||||||
res.json({ success: true, data: vehicle });
|
res.json({ success: true, data: vehicle });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -62,7 +69,7 @@ router.delete('/:id', authMiddleware, requireRole('admin'), async (req, res) =>
|
||||||
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
||||||
res.json({ success: true, message: '车辆已删除' });
|
res.json({ success: true, message: '车辆已删除' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -72,7 +79,7 @@ router.get('/status/:status', authMiddleware, async (req, res) => {
|
||||||
const vehicles = await Vehicle.find({ status: req.params.status });
|
const vehicles = await Vehicle.find({ status: req.params.status });
|
||||||
res.json({ success: true, data: vehicles });
|
res.json({ success: true, data: vehicles });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, message: error.message });
|
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -91,7 +98,7 @@ router.patch('/:id/location', authMiddleware, requireRole('admin', 'store'), asy
|
||||||
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
if (!vehicle) return res.status(404).json({ success: false, message: '车辆不存在' });
|
||||||
res.json({ success: true, data: vehicle });
|
res.json({ success: true, data: vehicle });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ success: false, message: error.message });
|
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ async function clearData() {
|
||||||
|
|
||||||
async function createVehicles() {
|
async function createVehicles() {
|
||||||
const vehicles = [
|
const vehicles = [
|
||||||
{ vehicleId: 'SCOOTER001', model: '黑骑士', color: '黑色', batteryType: '锂电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-01-15'), purchasePrice: 3500, purchaseSupplier: '小牛电动车' },
|
{ vehicleId: 'SCOOTER001', storeId: 'STORE001', frameNumber: 'FN20240001', model: '黑骑士', color: '黑色', batteryType: '锂电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-01-15'), purchasePrice: 3500, purchaseSupplier: '小牛电动车' },
|
||||||
{ vehicleId: 'SCOOTER002', model: '黑骑士', color: '白色', batteryType: '锂电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-02-20'), purchasePrice: 3500, purchaseSupplier: '小牛电动车' },
|
{ vehicleId: 'SCOOTER002', storeId: 'STORE001', frameNumber: 'FN20240002', model: '黑骑士', color: '白色', batteryType: '锂电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-02-20'), purchasePrice: 3500, purchaseSupplier: '小牛电动车' },
|
||||||
{ vehicleId: 'SCOOTER003', model: '电动车', color: '蓝色', batteryType: '铅酸电池', batteryCapacity: 24, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-03-10'), purchasePrice: 2800, purchaseSupplier: '雅迪电动车' },
|
{ vehicleId: 'SCOOTER003', storeId: 'STORE002', frameNumber: 'FN20240003', model: '电动车', color: '蓝色', batteryType: '铅酸电池', batteryCapacity: 24, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-03-10'), purchasePrice: 2800, purchaseSupplier: '雅迪电动车' },
|
||||||
{ vehicleId: 'SCOOTER004', model: '高端豪车', color: '红色', batteryType: '锂电池', batteryCapacity: 30, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-04-05'), purchasePrice: 8000, purchaseSupplier: '特斯拉' },
|
{ vehicleId: 'SCOOTER004', storeId: 'STORE002', frameNumber: 'FN20240004', model: '高端豪车', color: '红色', batteryType: '锂电池', batteryCapacity: 30, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-04-05'), purchasePrice: 8000, purchaseSupplier: '特斯拉' },
|
||||||
{ vehicleId: 'SCOOTER005', model: '普通标准套餐', color: '绿色', batteryType: '铅酸电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-05-01'), purchasePrice: 2500, purchaseSupplier: '爱玛电动车' }
|
{ vehicleId: 'SCOOTER005', storeId: 'STORE003', frameNumber: 'FN20240005', model: '普通标准套餐', color: '绿色', batteryType: '铅酸电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-05-01'), purchasePrice: 2500, purchaseSupplier: '爱玛电动车' }
|
||||||
];
|
];
|
||||||
console.log('🚗 创建示例车辆...');
|
console.log('🚗 创建示例车辆...');
|
||||||
await Vehicle.insertMany(vehicles);
|
await Vehicle.insertMany(vehicles);
|
||||||
|
|
@ -116,10 +116,10 @@ async function createRiders() {
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
await clearData();
|
await clearData();
|
||||||
|
await createStores();
|
||||||
await createVehicles();
|
await createVehicles();
|
||||||
await createCustomers();
|
await createCustomers();
|
||||||
await createOrders();
|
await createOrders();
|
||||||
await createStores();
|
|
||||||
await createRiders();
|
await createRiders();
|
||||||
console.log('\n🎉 示例数据创建完成!');
|
console.log('\n🎉 示例数据创建完成!');
|
||||||
console.log('车辆: 5 辆 | 客户: 5 个 | 订单: 3 个 | 门店: 3 个 | 骑手: 3 个');
|
console.log('车辆: 5 辆 | 客户: 5 个 | 订单: 3 个 | 门店: 3 个 | 骑手: 3 个');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue