// middleware/permissions.js —— 接口权限 + 数据范围 + 合同类型校验 + 层级检查 /** * 检查当前用户是否拥有指定权限 */ 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() } } /** * 根据用户角色级别和部门计算数据范围,挂到 req.scope * * 角色层级: * - 总经理(level=1): 全量数据 * - 部门经理(level=2): 本部门数据 * - 部门副经理(level=3): 本部门内自己及以下的数据 * - 一般专员(level=4): 仅自己的数据 */ function dataScope(resource) { return async (req, res, next) => { const { pool } = require('../db') const roleLevel = req.user.role_level || 4 const deptName = req.user.departmentName const deptDesc = req.user.departmentDesc // 总经理:全量数据 if (roleLevel === 1) { return next() } let sql = null let params = [] switch (resource) { case 'employees': if (roleLevel === 1) { // 总经理:全量数据(已在上面处理) } else if (roleLevel === 2) { if (deptName === 'hr') { // 人力资源部经理:查看所有员工 // 无过滤 } else { // 其他部门经理:本部门所有员工 sql = 'AND department = ?' params = [deptDesc] } } else if (roleLevel === 3) { // 部门副经理:本部门内非经理级别的员工 + 自己 sql = 'AND (department = ? AND position NOT LIKE ?) OR id = (SELECT employee_id FROM users WHERE id = ?)' params = [deptDesc, '%经理%', req.user.id] } else { // 一般专员:不能看到总经理的信息,但能看到其他员工 sql = 'AND position NOT LIKE ?' params = ['%总经理%'] } break case 'contracts': if (roleLevel === 2) { // 部门经理:本部门人员负责的合同 // 先查本部门的用户ID列表 const [deptUsers] = await pool.query( 'SELECT u.id FROM users u LEFT JOIN departments d ON u.department_id = d.id WHERE d.name = ?', [deptName] ) const deptUserIds = deptUsers.map(u => u.id) if (deptUserIds.length > 0) { sql = `AND responsible_user_id IN (${deptUserIds.map(() => '?').join(',')})` params = deptUserIds } else { sql = 'AND 1=0' // 无数据 } } else if (roleLevel >= 3) { // 副经理和专员:自己负责的合同 sql = 'AND responsible_user_id = ?' params = [req.user.id] } // 招商部/采购部的类型限制仍然通过 validateContractType 处理 break case 'after_sales': if (roleLevel === 2) { // 部门经理:本部门人员负责的售后 const [deptUsersAS] = await pool.query( 'SELECT u.id FROM users u LEFT JOIN departments d ON u.department_id = d.id WHERE d.name = ?', [deptName] ) const deptUserIdsAS = deptUsersAS.map(u => u.id) if (deptUserIdsAS.length > 0) { sql = `AND responsible_user_id IN (${deptUserIdsAS.map(() => '?').join(',')})` params = deptUserIdsAS } else { sql = 'AND 1=0' } } else if (roleLevel >= 3) { // 副经理和专员:自己负责的售后 sql = 'AND responsible_user_id = ?' params = [req.user.id] } break case 'customers': // 加盟商是公共资源,所有有权限的人都能看 // 但一般专员只能看自己关联的(通过合同/售后间接关联) if (roleLevel === 4) { // 一般专员:只能看到与自己负责的合同/售后关联的客户 sql = 'AND id IN (SELECT customer_id FROM contracts WHERE responsible_user_id = ? UNION SELECT customer_id FROM after_sales WHERE responsible_user_id = ?)' params = [req.user.id, req.user.id] } break } if (sql) { req.scope = { sql, params } } next() } } /** * 合同创建/修改时校验类型是否匹配部门 */ function validateContractType(req, res, next) { const deptName = req.user.departmentName const type = req.body.type if (type === undefined) return next() // 总经理和财务不限制 if (['general_manager', 'finance'].includes(deptName)) return next() // 信息技术部也不再是超级管理员,按部门权限走 if (deptName === 'admin') 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() } /** * 层级检查:确保操作者不能操作比自己级别高的用户 * 用于用户管理的删除/修改操作 */ function checkHierarchy(req, res, next) { return async (req, res, next) => { try { const { pool } = require('../db') const operatorLevel = req.user.role_level || 4 const targetId = Number(req.params.id) // 查询目标用户的role_level const [rows] = await pool.query('SELECT role_level FROM users WHERE id = ?', [targetId]) if (rows.length === 0) { return res.status(404).json({ code: 404, message: '用户不存在' }) } const targetLevel = rows[0].role_level || 4 // 不能操作比自己级别高的用户 if (targetLevel < operatorLevel) { return res.status(403).json({ code: 403, message: '不能操作比自己级别高的用户' }) } // 最高只能操作同级 if (targetLevel < operatorLevel) { return res.status(403).json({ code: 403, message: '权限不足,无法操作该用户' }) } next() } catch (e) { console.error('[checkHierarchy] error:', e) return res.status(500).json({ code: 500, message: e.message }) } } } module.exports = { checkPermission, dataScope, validateContractType, checkHierarchy }