2026-06-30 23:11:19 +08:00
// middleware/permissions.js —— 接口权限 + 数据范围 + 合同类型校验 + 层级检查
2026-06-24 22:44:14 +08:00
/**
* 检查当前用户是否拥有指定权限
*/
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-30 23:11:19 +08:00
* 根据用户角色级别和部门计算数据范围,挂到 req.scope
*
* 角色层级:
* - 总经理(level=1): 全量数据
* - 部门经理(level=2): 本部门数据
* - 部门副经理(level=3): 本部门内自己及以下的数据
* - 一般专员(level=4): 仅自己的数据
2026-06-24 22:44:14 +08:00
*/
2026-06-26 09:44:54 +08:00
function dataScope ( resource ) {
2026-06-30 23:11:19 +08:00
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
2026-06-24 22:44:14 +08:00
2026-06-30 23:11:19 +08:00
// 总经理:全量数据
if ( roleLevel === 1 ) {
2026-06-26 09:44:54 +08:00
return next ()
}
2026-06-24 22:44:14 +08:00
2026-06-30 23:11:19 +08:00
let sql = null
let params = []
2026-06-24 22:44:14 +08:00
2026-06-26 09:44:54 +08:00
switch ( resource ) {
2026-06-30 23:11:19 +08:00
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 = [ '%总经理%' ]
}
2026-06-26 09:44:54 +08:00
break
2026-06-24 22:44:14 +08:00
2026-06-26 09:44:54 +08:00
case 'contracts' :
2026-06-30 23:11:19 +08:00
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 ]
2026-06-26 09:44:54 +08:00
}
2026-06-30 23:11:19 +08:00
// 招商部/采购部的类型限制仍然通过 validateContractType 处理
2026-06-26 09:44:54 +08:00
break
2026-06-24 22:44:14 +08:00
2026-06-26 09:44:54 +08:00
case 'after_sales' :
2026-06-30 23:11:19 +08:00
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 ]
}
2026-06-26 09:44:54 +08:00
break
2026-06-24 22:44:14 +08:00
2026-06-30 23:11:19 +08:00
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 ]
2026-06-29 11:32:54 +08:00
}
2026-06-26 09:44:54 +08:00
break
}
2026-06-24 22:44:14 +08:00
2026-06-26 09:44:54 +08:00
if ( sql ) {
req . scope = { sql , params }
}
next ()
2026-06-24 22:44:14 +08:00
}
}
2026-06-26 09:44:54 +08:00
/**
* 合同创建/修改时校验类型是否匹配部门
*/
function validateContractType ( req , res , next ) {
const deptName = req . user . departmentName
const type = req . body . type
if ( type === undefined ) return next ()
2026-06-30 23:11:19 +08:00
// 总经理和财务不限制
if ([ 'general_manager' , 'finance' ]. includes ( deptName )) return next ()
// 信息技术部也不再是超级管理员,按部门权限走
if ( deptName === 'admin' ) return next ()
2026-06-26 09:44:54 +08:00
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 ()
}
2026-06-30 23:11:19 +08:00
/**
* 层级检查:确保操作者不能操作比自己级别高的用户
* 用于用户管理的删除/修改操作
*/
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 }