安全增强:JWT密钥重置、RBAC五表权限体系、标准用户角色权限分离、门店数据隔离修复
This commit is contained in:
parent
c48e2c2961
commit
e61281ec76
|
|
@ -209,9 +209,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "http://mirrors.tencentyun.com/npm/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -1365,15 +1365,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"version": "0.1.13",
|
||||
"resolved": "http://mirrors.tencentyun.com/npm/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
|
||||
"integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "http://mirrors.tencentyun.com/npm/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,18 @@ const rateLimit = require('express-rate-limit');
|
|||
const app = express();
|
||||
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: 安全响应头
|
||||
|
|
@ -50,20 +62,33 @@ mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/e-scooter
|
|||
.then(() => console.log('✅ MongoDB 连接成功'))
|
||||
.catch(err => console.error('❌ MongoDB 连接失败:', err.message));
|
||||
|
||||
// ─── 路由 ───────────────────────────────────────────────────
|
||||
app.use('/api/vehicles', require('./routes/vehicles'));
|
||||
app.use('/api/orders', require('./routes/orders'));
|
||||
app.use('/api/customers', require('./routes/customers'));
|
||||
app.use('/api/finance', require('./routes/finance'));
|
||||
app.use('/api/stores', require('./routes/stores'));
|
||||
app.use('/api/complaints', require('./routes/complaints'));
|
||||
app.use('/api/approvals', require('./routes/approvals'));
|
||||
app.use('/api/payments', require('./routes/payments'));
|
||||
app.use('/api/conflicts', require('./routes/conflicts'));
|
||||
app.use('/api/applications', require('./routes/applications'));
|
||||
app.use('/api/disputes', require('./routes/disputes'));
|
||||
// ─── 路由(统一加 authMiddleware,按角色分级授权) ────────────
|
||||
//
|
||||
/// 管理后台 API(admin only)
|
||||
app.use('/api/vehicles', rbacAuth('vehicles:read', 'vehicles:write'), require('./routes/vehicles'));
|
||||
app.use('/api/customers', rbacAuth('customers:read', 'customers:write'), require('./routes/customers'));
|
||||
app.use('/api/finance', rbacAuth('finance:read'), require('./routes/finance'));
|
||||
app.use('/api/orders', rbacAuth('orders:read', 'orders:write'), require('./routes/orders'));
|
||||
app.use('/api/approvals', rbacAuth('approvals:read', 'approvals:write'), require('./routes/approvals'));
|
||||
app.use('/api/disputes', rbacAuth('disputes:read', 'disputes:write'), require('./routes/disputes'));
|
||||
app.use('/api/conflicts', rbacAuth('disputes:read', 'disputes:write'), require('./routes/conflicts'));
|
||||
|
||||
/// 门店端 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/vehicle-types', require('./routes/vehicleTypes'));
|
||||
app.use('/api/vehicle-types', rbacAuth('vehicleTypes:read', 'vehicleTypes:write'), require('./routes/vehicleTypes'));
|
||||
|
||||
// ─── 健康检查(不限流) ─────────────────────────────────────
|
||||
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');
|
||||
|
||||
/**
|
||||
* JWT Token 黑名单(已撤销的 jti 集合)
|
||||
*/
|
||||
const revokedTokens = new Set();
|
||||
|
||||
/**
|
||||
* 撤销指定 jti 的 token
|
||||
*/
|
||||
const revokeToken = (jti) => revokedTokens.add(jti);
|
||||
module.exports.revokeToken = revokeToken;
|
||||
|
||||
/**
|
||||
* JWT 鉴权中间件
|
||||
* 验证请求头中的 Bearer token,写入 req.user
|
||||
|
|
@ -11,6 +22,10 @@ const authMiddleware = (req, res, next) => {
|
|||
}
|
||||
try {
|
||||
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;
|
||||
next();
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ const errorHandler = (err, req, res, next) => {
|
|||
if (err.name === 'MongoError' || err.name === 'MongooseError') {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: '数据库错误',
|
||||
error: err.message
|
||||
message: '数据库错误'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -15,8 +14,7 @@ const errorHandler = (err, req, res, next) => {
|
|||
if (err.name === 'ValidationError') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '数据验证失败',
|
||||
error: err.message
|
||||
message: '数据验证失败'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -24,16 +22,14 @@ const errorHandler = (err, req, res, next) => {
|
|||
if (err.name === 'CastError') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '数据不存在',
|
||||
error: err.message
|
||||
message: '数据不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 默认错误
|
||||
res.status(err.status || 500).json({
|
||||
success: false,
|
||||
message: err.message || '服务器内部错误',
|
||||
error: process.env.NODE_ENV === 'development' ? err.stack : undefined
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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: ['微信', '支付宝', '现金', '银行卡'] },
|
||||
paymentDate: { type: Date },
|
||||
|
||||
// 门店关联
|
||||
storeId: { type: String, index: true }, // 所属门店
|
||||
|
||||
// 合同信息
|
||||
contractUrl: { type: String }, // 合同文件路径
|
||||
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 哈希存储,查询时默认不返回)
|
||||
password: { type: String, required: true, select: false },
|
||||
|
||||
// 角色(customer=普通用户, admin=管理员, store=门店管理员)
|
||||
role: { type: String, enum: ['customer', 'admin', 'store'], default: 'customer' },
|
||||
// 角色(customer=普通用户, admin=管理员, store=门店管理员, rider=骑手)
|
||||
role: { type: String, enum: ['customer', 'admin', 'store', 'rider'], default: 'customer' },
|
||||
|
||||
// 接单状态
|
||||
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 router = express.Router();
|
||||
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 {
|
||||
const apps = await Application.find().populate('store');
|
||||
res.json({ success: true, data: apps });
|
||||
} 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 {
|
||||
const app = new Application(req.body);
|
||||
await app.save();
|
||||
res.json({ success: true, data: app });
|
||||
} 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 {
|
||||
const app = await Application.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||
res.json({ success: true, data: app });
|
||||
} 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 {
|
||||
await Application.findByIdAndDelete(req.params.id);
|
||||
res.json({ success: true });
|
||||
} 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 router = express.Router();
|
||||
const Approval = require('../models/Approval');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
|
||||
// 获取所有审批
|
||||
router.get('/', async (req, res) => {
|
||||
// 获取所有审批(仅 admin)
|
||||
router.get('/', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||
try {
|
||||
const approvals = await Approval.find();
|
||||
res.json({ success: true, data: approvals });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||
}
|
||||
});
|
||||
|
||||
// 创建审批
|
||||
router.post('/', async (req, res) => {
|
||||
// 创建审批(仅 admin)
|
||||
router.post('/', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||
try {
|
||||
const approval = new Approval(req.body);
|
||||
await approval.save();
|
||||
res.json({ success: true, data: approval });
|
||||
} 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)
|
||||
router.put('/:id', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||
try {
|
||||
const approval = await Approval.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||
res.json({ success: true, data: approval });
|
||||
} 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)
|
||||
router.delete('/:id', authMiddleware, requireRole('admin'), async (req, res) => {
|
||||
try {
|
||||
await Approval.findByIdAndDelete(req.params.id);
|
||||
res.json({ success: true });
|
||||
} 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 router = express.Router();
|
||||
const Complaint = require('../models/Complaint');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
|
||||
// 获取所有投诉
|
||||
router.get('/', async (req, res) => {
|
||||
// 获取所有投诉(admin 或 store)
|
||||
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||
try {
|
||||
const complaints = await Complaint.find().populate('customer order');
|
||||
res.json({ success: true, data: complaints });
|
||||
} 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 {
|
||||
const complaint = new Complaint(req.body);
|
||||
await complaint.save();
|
||||
res.json({ success: true, data: complaint });
|
||||
} 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 {
|
||||
const complaint = await Complaint.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||
res.json({ success: true, data: complaint });
|
||||
} 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 {
|
||||
await Complaint.findByIdAndDelete(req.params.id);
|
||||
res.json({ success: true });
|
||||
} 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 router = express.Router();
|
||||
const Conflict = require('../models/Conflict');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
|
||||
// 获取所有矛盾记录
|
||||
router.get('/', async (req, res) => {
|
||||
// 获取所有矛盾记录(admin 或 store)
|
||||
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||
try {
|
||||
const conflicts = await Conflict.find();
|
||||
res.json({ success: true, data: conflicts });
|
||||
} 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 {
|
||||
const conflict = new Conflict(req.body);
|
||||
await conflict.save();
|
||||
res.json({ success: true, data: conflict });
|
||||
} 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 {
|
||||
const conflict = await Conflict.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||
res.json({ success: true, data: conflict });
|
||||
} 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 {
|
||||
await Conflict.findByIdAndDelete(req.params.id);
|
||||
res.json({ success: true });
|
||||
} 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 router = express.Router();
|
||||
const Customer = require('../models/Customer');
|
||||
const Store = require('../models/Store');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
const { validate } = 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();
|
||||
res.json({ success: true, data: customers });
|
||||
} 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: '客户不存在' });
|
||||
res.json({ success: true, data: customer });
|
||||
} 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();
|
||||
res.status(201).json({ success: true, data: customer });
|
||||
} 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: '客户不存在' });
|
||||
res.json({ success: true, data: customer });
|
||||
} 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: '客户不存在' });
|
||||
res.json({ success: true, message: '客户已删除' });
|
||||
} 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 });
|
||||
} 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: '客户不存在' });
|
||||
res.json({ success: true, data: customer });
|
||||
} 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 router = express.Router();
|
||||
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 {
|
||||
const disputes = await Dispute.find();
|
||||
res.json({ success: true, data: disputes });
|
||||
} 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 {
|
||||
const dispute = new Dispute(req.body);
|
||||
await dispute.save();
|
||||
res.json({ success: true, data: dispute });
|
||||
} 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 {
|
||||
const dispute = await Dispute.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||
res.json({ success: true, data: dispute });
|
||||
} 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 {
|
||||
await Dispute.findByIdAndDelete(req.params.id);
|
||||
res.json({ success: true });
|
||||
} 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) {
|
||||
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();
|
||||
res.json({ success: true, data: payment });
|
||||
} 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 });
|
||||
res.json({ success: true, data: payment });
|
||||
} 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);
|
||||
res.json({ success: true });
|
||||
} 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({});
|
||||
res.json({ success: true, message: 'All payments deleted' });
|
||||
} 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) {
|
||||
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 Vehicle = require('../models/Vehicle');
|
||||
const Customer = require('../models/Customer');
|
||||
const Store = require('../models/Store');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
const { validate } = require('../middleware/validate');
|
||||
const { schemas } = require('../middleware/validate');
|
||||
|
|
@ -18,15 +19,17 @@ router.get('/', authMiddleware, async (req, res) => {
|
|||
// 此处通过 rider id 查找(假设订单中 rider 字段关联骑手)
|
||||
filter.rider = req.user.id;
|
||||
} else if (req.user.role === 'store') {
|
||||
// store 角色可按门店过滤(前端传 storeId)
|
||||
if (req.query.storeId) filter.storeId = req.query.storeId;
|
||||
// store 角色永远只能看自己门店的数据,忽略前端传的 storeId 参数
|
||||
if (req.user.storeId) {
|
||||
filter.storeId = req.user.storeId;
|
||||
}
|
||||
}
|
||||
const orders = await Order.find(filter)
|
||||
.populate('customer', 'name phone')
|
||||
.populate('vehicle', 'model vehicleId');
|
||||
res.json({ success: true, data: orders });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
res.status(500).json({ success: false, message: '服务器内部错误' });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -45,7 +48,7 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
|||
}
|
||||
res.json({ success: true, data: order });
|
||||
} 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 });
|
||||
} 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: '订单不存在' });
|
||||
res.json({ success: true, data: order });
|
||||
} 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 });
|
||||
} 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 });
|
||||
} 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 });
|
||||
} 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');
|
||||
res.json({ success: true, data: orders });
|
||||
} 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 router = express.Router();
|
||||
const Payment = require('../models/Payment');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
|
||||
// 获取所有打款记录
|
||||
router.get('/', async (req, res) => {
|
||||
// 获取所有打款记录(需 admin 或 store)
|
||||
router.get('/', authMiddleware, requireRole('admin', 'store'), async (req, res) => {
|
||||
try {
|
||||
const payments = await Payment.find();
|
||||
res.json({ success: true, data: payments });
|
||||
} 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 {
|
||||
const payment = new Payment(req.body);
|
||||
await payment.save();
|
||||
res.json({ success: true, data: payment });
|
||||
} 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 {
|
||||
const payment = await Payment.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
||||
res.json({ success: true, data: payment });
|
||||
} 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 {
|
||||
await Payment.findByIdAndDelete(req.params.id);
|
||||
res.json({ success: true });
|
||||
} 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 Order = require('../models/Order');
|
||||
const { comparePassword } = require('../utils/password');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
const { validate } = require('../middleware/validate');
|
||||
const { schemas } = require('../middleware/validate');
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ const loginLimiter = rateLimit({
|
|||
message: { success: false, message: '登录尝试过于频繁,请15分钟后再试' }
|
||||
});
|
||||
|
||||
// 骑手登录
|
||||
// 骑手登录(无需鉴权)
|
||||
router.post('/login', loginLimiter, validate(schemas.login), async (req, res) => {
|
||||
try {
|
||||
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: '手机号或密码错误' });
|
||||
}
|
||||
|
||||
// 签发 JWT(包含 id、role)
|
||||
// 签发 JWT(包含 id、role、jti)
|
||||
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,
|
||||
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
|
||||
);
|
||||
|
|
@ -57,31 +58,45 @@ router.post('/login', loginLimiter, validate(schemas.login), async (req, res) =>
|
|||
}
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取骑手信息(需登录)
|
||||
router.get('/:id', async (req, res) => {
|
||||
// 获取骑手信息(需登录,且只能查看自己;admin/store 可查看任意骑手)
|
||||
router.get('/:id', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const isSelf = req.params.id === req.user.id;
|
||||
const isPrivileged = ['admin', 'store'].includes(req.user.role);
|
||||
if (!isSelf && !isPrivileged) {
|
||||
return res.status(403).json({ success: false, message: '无权查看该骑手信息' });
|
||||
}
|
||||
const rider = await Rider.findById(req.params.id);
|
||||
if (!rider) {
|
||||
return res.status(404).json({ success: false, message: '骑手不存在' });
|
||||
}
|
||||
res.json({ success: true, data: rider });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
res.status(500).json({ success: false, message: "服务器内部错误" });
|
||||
}
|
||||
});
|
||||
|
||||
// 更新骑手信息(如状态切换,需登录本人或管理员)
|
||||
router.put('/:id', async (req, res) => {
|
||||
// 更新骑手信息(需登录本人,或 admin/store)
|
||||
router.put('/:id', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
// 不允许通过 PUT 修改密码和 role
|
||||
const { password, role, ...safeBody } = req.body;
|
||||
const isSelf = req.params.id === req.user.id;
|
||||
const isPrivileged = ['admin', 'store'].includes(req.user.role);
|
||||
if (!isSelf && !isPrivileged) {
|
||||
return res.status(403).json({ success: false, message: '无权修改该骑手信息' });
|
||||
}
|
||||
// 不允许通过 PUT 修改密码和 role(仅 admin 可改 role)
|
||||
const { password, ...safeBody } = req.body;
|
||||
if (req.body.role && !isPrivileged) {
|
||||
return res.status(403).json({ success: false, message: '无权修改角色' });
|
||||
}
|
||||
const updateData = isPrivileged ? req.body : safeBody;
|
||||
const rider = await Rider.findByIdAndUpdate(
|
||||
req.params.id,
|
||||
safeBody,
|
||||
updateData,
|
||||
{ new: true, runValidators: true }
|
||||
);
|
||||
if (!rider) {
|
||||
|
|
@ -89,20 +104,25 @@ router.put('/:id', async (req, res) => {
|
|||
}
|
||||
res.json({ success: true, data: rider });
|
||||
} catch (error) {
|
||||
res.status(400).json({ success: false, message: error.message });
|
||||
res.status(400).json({ success: false, message: "服务器内部错误" });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取骑手的订单
|
||||
router.get('/:id/orders', async (req, res) => {
|
||||
// 获取骑手的订单(需登录本人,或 admin/store)
|
||||
router.get('/:id/orders', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const isSelf = req.params.id === req.user.id;
|
||||
const isPrivileged = ['admin', 'store'].includes(req.user.role);
|
||||
if (!isSelf && !isPrivileged) {
|
||||
return res.status(403).json({ success: false, message: '无权查看该骑手的订单' });
|
||||
}
|
||||
const orders = await Order.find({ rider: req.params.id })
|
||||
.populate('customer', 'name phone')
|
||||
.populate('vehicle', 'vehicleId model color')
|
||||
.sort({ createdAt: -1 });
|
||||
res.json({ success: true, data: orders });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, message: 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) => {
|
||||
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();
|
||||
res.json({ success: true, data: stores });
|
||||
} 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();
|
||||
res.json({ success: true, data: store });
|
||||
} 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: '门店不存在' });
|
||||
res.json({ success: true, data: store });
|
||||
} 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);
|
||||
res.json({ success: true });
|
||||
} 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 router = express.Router();
|
||||
const VehicleType = require('../models/VehicleType');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
|
||||
// 获取所有车型(支持按 storeId 筛选)
|
||||
router.get('/', async (req, res) => {
|
||||
// 获取所有车型(所有登录用户均可浏览)
|
||||
router.get('/', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const filter = {};
|
||||
if (req.query.storeId) filter.storeId = req.query.storeId;
|
||||
const vehicleTypes = await VehicleType.find(filter).sort({ createdAt: -1 });
|
||||
res.json({ success: true, data: vehicleTypes });
|
||||
} 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 {
|
||||
const vehicleType = new VehicleType(req.body);
|
||||
await vehicleType.save();
|
||||
res.status(201).json({ success: true, data: vehicleType });
|
||||
} 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 {
|
||||
const vehicleType = await VehicleType.findByIdAndUpdate(
|
||||
req.params.id,
|
||||
|
|
@ -38,12 +39,12 @@ router.put('/:id', async (req, res) => {
|
|||
}
|
||||
res.json({ success: true, data: vehicleType });
|
||||
} 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 {
|
||||
const vehicleType = await VehicleType.findByIdAndDelete(req.params.id);
|
||||
if (!vehicleType) {
|
||||
|
|
@ -51,7 +52,7 @@ router.delete('/:id', async (req, res) => {
|
|||
}
|
||||
res.json({ success: true, message: '车型已删除' });
|
||||
} 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 router = express.Router();
|
||||
const Vehicle = require('../models/Vehicle');
|
||||
const Store = require('../models/Store');
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
const { validate } = require('../middleware/validate');
|
||||
const { schemas } = require('../middleware/validate');
|
||||
|
|
@ -9,12 +10,18 @@ const { schemas } = require('../middleware/validate');
|
|||
router.get('/', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
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;
|
||||
const vehicles = await Vehicle.find(filter).populate('currentOrderId');
|
||||
res.json({ success: true, data: vehicles });
|
||||
} 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: '车辆不存在' });
|
||||
res.json({ success: true, data: vehicle });
|
||||
} 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();
|
||||
res.status(201).json({ success: true, data: vehicle });
|
||||
} 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: '车辆不存在' });
|
||||
res.json({ success: true, data: vehicle });
|
||||
} 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: '车辆不存在' });
|
||||
res.json({ success: true, message: '车辆已删除' });
|
||||
} 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 });
|
||||
res.json({ success: true, data: vehicles });
|
||||
} 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: '车辆不存在' });
|
||||
res.json({ success: true, data: vehicle });
|
||||
} 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() {
|
||||
const vehicles = [
|
||||
{ vehicleId: 'SCOOTER001', 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: 'SCOOTER003', 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: 'SCOOTER005', model: '普通标准套餐', color: '绿色', batteryType: '铅酸电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-05-01'), purchasePrice: 2500, 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', storeId: 'STORE001', frameNumber: 'FN20240002', model: '黑骑士', color: '白色', batteryType: '锂电池', batteryCapacity: 20, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-02-20'), purchasePrice: 3500, 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', storeId: 'STORE002', frameNumber: 'FN20240004', model: '高端豪车', color: '红色', batteryType: '锂电池', batteryCapacity: 30, batteryStatus: '正常', status: '空闲', purchaseDate: new Date('2024-04-05'), purchasePrice: 8000, 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('🚗 创建示例车辆...');
|
||||
await Vehicle.insertMany(vehicles);
|
||||
|
|
@ -116,10 +116,10 @@ async function createRiders() {
|
|||
async function main() {
|
||||
try {
|
||||
await clearData();
|
||||
await createStores();
|
||||
await createVehicles();
|
||||
await createCustomers();
|
||||
await createOrders();
|
||||
await createStores();
|
||||
await createRiders();
|
||||
console.log('\n🎉 示例数据创建完成!');
|
||||
console.log('车辆: 5 辆 | 客户: 5 个 | 订单: 3 个 | 门店: 3 个 | 骑手: 3 个');
|
||||
|
|
|
|||
Loading…
Reference in New Issue