重构后端,使其更加权责分明

This commit is contained in:
2026-06-26 09:44:54 +08:00
parent 764919f69e
commit 89d8505260
11 changed files with 1426 additions and 761 deletions

View File

@@ -1,4 +1,4 @@
// middleware/permissions.js —— RBAC 权限检查 + 数据范围过滤
// middleware/permissions.js —— 接口权限 + 数据范围 + 合同类型校验
/**
* 检查当前用户是否拥有指定权限
@@ -16,58 +16,81 @@ function checkPermission(resource, action) {
}
/**
* 根据当前用户角色,返回该资源的数据范围过滤条件
* @param {object} user - req.user含 roleName, id, department
* @param {string} resource - 资源名customers / contracts / after_sales / products / suppliers / employees / users
* @returns {{ where?: string, values?: any[], deny?: boolean }}
* - where + values追加到 SQL WHERE 子句的条件
* - denytrue 表示该角色无权访问此资源
* 根据用户部门计算数据范围,挂到 req.scope
* @param {string} resource - 资源名customers / contracts / after_sales / employees
*/
function getDataScope(user, resource) {
const { roleName, id: userId, department } = user
function dataScope(resource) {
return (req, res, next) => {
const deptName = req.user.departmentName // 如 'franchise_manager'
// admin 和 general_manager总经理:全量数据,无限制
if (roleName === 'admin' || roleName === 'general_manager') {
return {}
}
// 信息技术部和总经理办公室:全量数据
if (deptName === 'admin' || deptName === 'general_manager') {
return next()
}
switch (resource) {
case 'customers':
if (roleName === 'finance') return {}
return { where: 'responsible_user_id = ?', values: [userId] }
let sql = null, params = []
case 'contracts':
if (roleName === 'finance') return {}
if (roleName === 'procurement_manager') {
return { where: 'type = ?', values: ['supply'] }
}
if (roleName === 'franchise_manager' || roleName === 'operations_manager') {
return { where: 'responsible_user_id = ? AND type = ?', values: [userId, 'franchise'] }
}
return {}
switch (resource) {
case 'customers':
// 过渡期:招商部和运营部也能看全量加盟商
break
case 'after_sales':
if (roleName === 'finance') return {}
if (roleName === 'operations_manager') {
return { where: 'responsible_user_id = ?', values: [userId], tableAlias: 'a' }
}
// franchise_manager 和 procurement_manager 无权限checkPermission 已拦截)
return {}
case 'contracts':
if (deptName === 'franchise_manager') {
sql = 'AND type = ?'
params = ['franchise']
} else if (deptName === 'procurement_manager') {
sql = 'AND type = ?'
params = ['supply']
}
// finance 全量看operations_manager 无合同权限checkPermission 已拦截)
break
case 'employees':
if (roleName === 'finance') return {}
// 所有非管理员/总经理角色:只能看自己部门的员工
return { where: 'department = ?', values: [department] }
case 'after_sales':
// 过渡期:运营部看全量售后
break
case 'products':
case 'suppliers':
case 'users':
// 无数据范围过滤,仅靠 checkPermission 控制访问
return {}
case 'employees':
if (['franchise_manager', 'operations_manager', 'procurement_manager'].includes(deptName)) {
sql = 'AND department = ?'
params = [req.user.departmentDesc] // 如 '招商部'
}
// finance 全量看员工
break
}
default:
return {}
if (sql) {
req.scope = { sql, params }
}
next()
}
}
module.exports = { checkPermission, getDataScope }
/**
* 合同创建/修改时校验类型是否匹配部门
* 招商部只能操作加盟合同,采购部只能操作采购合同
*/
function validateContractType(req, res, next) {
const deptName = req.user.departmentName
const type = req.body.type
// 未传 type 则跳过(路由会默认 'franchise'
if (type === undefined) return next()
// 管理员/总经理/财务不限制
if (['admin', 'general_manager', 'finance'].includes(deptName)) return next()
// 采购部只能操作采购合同
if (deptName === 'procurement_manager' && type !== 'supply') {
return res.status(403).json({ code: 403, message: '采购部只能操作采购合同' })
}
// 招商部只能操作加盟合同
if (deptName === 'franchise_manager' && type !== 'franchise') {
return res.status(403).json({ code: 403, message: '招商部只能操作加盟合同' })
}
next()
}
module.exports = { checkPermission, dataScope, validateContractType }