2026-06-26 09:44:54 +08:00
|
|
|
|
// middleware/permissions.js —— 接口权限 + 数据范围 + 合同类型校验
|
2026-06-24 22:44:14 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查当前用户是否拥有指定权限
|
|
|
|
|
|
* @param {string} resource - 资源名,如 'customer'
|
|
|
|
|
|
* @param {string} action - 操作名,如 'read'
|
|
|
|
|
|
*/
|
|
|
|
|
|
function checkPermission(resource, action) {
|
|
|
|
|
|
return (req, res, next) => {
|
|
|
|
|
|
const permName = `${resource}:${action}`
|
|
|
|
|
|
if (!req.user || !req.user.permissions || !req.user.permissions.includes(permName)) {
|
|
|
|
|
|
return res.status(403).json({ code: 403, message: '权限不足' })
|
|
|
|
|
|
}
|
|
|
|
|
|
next()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-26 09:44:54 +08:00
|
|
|
|
* 根据用户部门计算数据范围,挂到 req.scope
|
|
|
|
|
|
* @param {string} resource - 资源名:customers / contracts / after_sales / employees
|
2026-06-24 22:44:14 +08:00
|
|
|
|
*/
|
2026-06-26 09:44:54 +08:00
|
|
|
|
function dataScope(resource) {
|
|
|
|
|
|
return (req, res, next) => {
|
|
|
|
|
|
const deptName = req.user.departmentName // 如 'franchise_manager'
|
|
|
|
|
|
|
|
|
|
|
|
// 信息技术部和总经理办公室:全量数据
|
|
|
|
|
|
if (deptName === 'admin' || deptName === 'general_manager') {
|
|
|
|
|
|
return next()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let sql = null, params = []
|
|
|
|
|
|
|
|
|
|
|
|
switch (resource) {
|
|
|
|
|
|
case 'customers':
|
|
|
|
|
|
// 过渡期:招商部和运营部也能看全量加盟商
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
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 'after_sales':
|
|
|
|
|
|
// 过渡期:运营部看全量售后
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
case 'employees':
|
2026-06-29 11:32:54 +08:00
|
|
|
|
if (deptName === 'hr') {
|
|
|
|
|
|
// 人力资源部可查看全量员工
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
2026-06-26 09:44:54 +08:00
|
|
|
|
if (['franchise_manager', 'operations_manager', 'procurement_manager'].includes(deptName)) {
|
|
|
|
|
|
sql = 'AND department = ?'
|
|
|
|
|
|
params = [req.user.departmentDesc] // 如 '招商部'
|
|
|
|
|
|
}
|
2026-06-29 11:32:54 +08:00
|
|
|
|
// admin / general_manager / finance 全量看员工
|
2026-06-26 09:44:54 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sql) {
|
|
|
|
|
|
req.scope = { sql, params }
|
|
|
|
|
|
}
|
|
|
|
|
|
next()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 合同创建/修改时校验类型是否匹配部门
|
|
|
|
|
|
* 招商部只能操作加盟合同,采购部只能操作采购合同
|
|
|
|
|
|
*/
|
|
|
|
|
|
function validateContractType(req, res, next) {
|
|
|
|
|
|
const deptName = req.user.departmentName
|
|
|
|
|
|
const type = req.body.type
|
|
|
|
|
|
|
|
|
|
|
|
// 未传 type 则跳过(路由会默认 'franchise')
|
|
|
|
|
|
if (type === undefined) return next()
|
2026-06-24 22:44:14 +08:00
|
|
|
|
|
2026-06-26 09:44:54 +08:00
|
|
|
|
// 管理员/总经理/财务不限制
|
|
|
|
|
|
if (['admin', 'general_manager', 'finance'].includes(deptName)) return next()
|
|
|
|
|
|
|
|
|
|
|
|
// 采购部只能操作采购合同
|
|
|
|
|
|
if (deptName === 'procurement_manager' && type !== 'supply') {
|
|
|
|
|
|
return res.status(403).json({ code: 403, message: '采购部只能操作采购合同' })
|
2026-06-24 22:44:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 09:44:54 +08:00
|
|
|
|
// 招商部只能操作加盟合同
|
|
|
|
|
|
if (deptName === 'franchise_manager' && type !== 'franchise') {
|
|
|
|
|
|
return res.status(403).json({ code: 403, message: '招商部只能操作加盟合同' })
|
2026-06-24 22:44:14 +08:00
|
|
|
|
}
|
2026-06-26 09:44:54 +08:00
|
|
|
|
|
|
|
|
|
|
next()
|
2026-06-24 22:44:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 09:44:54 +08:00
|
|
|
|
module.exports = { checkPermission, dataScope, validateContractType }
|