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

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,6 +1,5 @@
// routes/afterSales.js —— 售后管理 CRUD
// routes/afterSales.js —— 售后管理 CRUD(纯数据库操作,权限由中间件层控制)
const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
@@ -46,16 +45,10 @@ async function list(req, res) {
let where = 'WHERE 1=1'
const params = []
// 数据范围过滤
const scope = getDataScope(req.user, 'after_sales')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
// list 查询有 JOIN需要表别名前缀避免列名歧义
const scopeCol = scope.tableAlias ? scope.where.replace(/\bresponsible_user_id\b/g, `${scope.tableAlias}.responsible_user_id`) : scope.where
where += ' AND ' + scopeCol
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope已含表别名前缀
if (req.scope && req.scope.sql) {
where += ' ' + req.scope.sql
params.push(...req.scope.params)
}
if (id) {
@@ -115,14 +108,10 @@ async function detail(req, res) {
let sql = `${LIST_SELECT} WHERE a.id = ?`
const params = [req.params.id]
const scope = getDataScope(req.user, 'after_sales')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
const scopeCol = scope.tableAlias ? scope.where.replace('responsible_user_id', `${scope.tableAlias}.responsible_user_id`) : scope.where
sql += ' AND ' + scopeCol
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope已含表别名前缀
if (req.scope && req.scope.sql) {
sql += ' ' + req.scope.sql
params.push(...req.scope.params)
}
const [rows] = await pool.query(sql, params)
@@ -140,7 +129,7 @@ async function detail(req, res) {
async function create(req, res) {
const {
customer_id, feedback, employee_id, handle_method,
handle_status, service_date, remark, responsible_user_id,
handle_status, service_date, remark,
} = req.body || {}
if (!customer_id) {
@@ -156,17 +145,20 @@ async function create(req, res) {
if (cust.length === 0) {
return res.status(400).json({ code: 400, message: '关联客户不存在' })
}
// 校验业务员
// 校验业务员,并通过 employee_id 自动推导负责人
let ownerId = req.user.id
if (employee_id) {
const [emp] = await pool.query('SELECT id FROM employees WHERE id = ?', [employee_id])
if (emp.length === 0) {
return res.status(400).json({ code: 400, message: '关联业务员不存在' })
}
// 业务员即负责人:查找该员工对应的系统用户
const [userRows] = await pool.query('SELECT id FROM users WHERE employee_id = ? LIMIT 1', [employee_id])
if (userRows.length > 0) {
ownerId = userRows[0].id
}
}
// 如果没指定负责人,默认设为当前用户
const ownerId = responsible_user_id || req.user.id
const [result] = await pool.query(
`INSERT INTO after_sales
(customer_id, feedback, employee_id, handle_method, handle_status, service_date, remark, responsible_user_id)
@@ -198,15 +190,11 @@ async function update(req, res) {
try {
// 确认记录存在且在数据范围内
let checkSql = 'SELECT id FROM after_sales WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'after_sales')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkSql = 'SELECT id, employee_id FROM after_sales WHERE id = ?'
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {
@@ -226,6 +214,22 @@ async function update(req, res) {
}
}
// 业务员即负责人:当 employee_id 变更时自动推导 responsible_user_id
if (req.body.employee_id !== undefined) {
if (req.body.employee_id) {
const [userRows] = await pool.query('SELECT id FROM users WHERE employee_id = ? LIMIT 1', [req.body.employee_id])
if (userRows.length > 0) {
req.body.responsible_user_id = userRows[0].id
} else {
// 该员工无对应系统用户时,设为当前登录用户
req.body.responsible_user_id = req.user.id
}
} else {
// 业务员被清空时,清空负责人
req.body.responsible_user_id = null
}
}
const sets = []
const params = []
for (const f of fields) {
@@ -259,14 +263,10 @@ async function remove(req, res) {
const { id } = req.params
try {
let checkSql = 'SELECT id FROM after_sales WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'after_sales')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {

View File

@@ -1,6 +1,5 @@
// routes/contracts.js —— 合同管理 CRUD
// routes/contracts.js —— 合同管理 CRUD(纯数据库操作,权限由中间件层控制)
const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
@@ -55,16 +54,10 @@ async function list(req, res) {
let where = 'WHERE 1=1'
const params = []
// 数据范围过滤(列表查询使用表别名 c需要加前缀)
const scope = getDataScope(req.user, 'contracts')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
// 给 scope.where 中的字段加上表别名 c.
const scopedWhere = scope.where.replace(/\b(responsible_user_id|type)\b/g, 'c.$1')
where += ' AND ' + scopedWhere
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope无别名list/detail 需机械加 c. 前缀)
if (req.scope && req.scope.sql) {
where += ' ' + req.scope.sql.replace(/\btype\b/g, 'c.type')
params.push(...req.scope.params)
}
if (id) {
@@ -139,15 +132,10 @@ async function detail(req, res) {
let sql = `${DETAIL_SELECT} WHERE c.id = ?`
const params = [req.params.id]
const scope = getDataScope(req.user, 'contracts')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
// detail 查询使用表别名 c需要给 scope.where 加上前缀
const scopedWhere = scope.where.replace(/\b(responsible_user_id|type)\b/g, 'c.$1')
sql += ' AND ' + scopedWhere
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope无别名detail 需机械加 c. 前缀)
if (req.scope && req.scope.sql) {
sql += ' ' + req.scope.sql.replace(/\btype\b/g, 'c.type')
params.push(...req.scope.params)
}
const [rows] = await pool.query(sql, params)
@@ -168,14 +156,8 @@ async function create(req, res) {
amount, effective_date, expiry_date, employee_id, status, remark,
} = req.body || {}
// 根据角色限制合同类型
const contractType = req.body.type || (req.user.roleName === 'procurement_manager' ? 'supply' : 'franchise')
if (req.user.roleName === 'procurement_manager' && contractType !== 'supply') {
return res.status(403).json({ code: 403, message: '采购经理只能创建采购合同' })
}
if (req.user.roleName === 'franchise_manager' && contractType !== 'franchise') {
return res.status(403).json({ code: 403, message: '招商经理只能创建加盟合同' })
}
// 合同类型默认值(类型校验由中间件层控制)
const contractType = req.body.type || 'franchise'
if (contractType === 'supply' && !supplier_id) {
return res.status(400).json({ code: 400, message: '采购合同必须选择供应商' })
@@ -202,12 +184,18 @@ async function create(req, res) {
return res.status(400).json({ code: 400, message: '关联供应商不存在' })
}
}
// 校验业务员(如果填了)
// 校验业务员(如果填了),并通过 employee_id 自动推导负责人
let responsibleUserId = req.user.id
if (employee_id) {
const [emp] = await pool.query('SELECT id FROM employees WHERE id = ?', [employee_id])
if (emp.length === 0) {
return res.status(400).json({ code: 400, message: '关联业务员不存在' })
}
// 业务员即负责人:查找该员工对应的系统用户
const [userRows] = await pool.query('SELECT id FROM users WHERE employee_id = ? LIMIT 1', [employee_id])
if (userRows.length > 0) {
responsibleUserId = userRows[0].id
}
}
const [result] = await pool.query(
@@ -224,7 +212,7 @@ async function create(req, res) {
status || '生效中',
remark || null,
contractType,
req.body.responsible_user_id || req.user.id]
responsibleUserId]
)
const [rows] = await pool.query(`${DETAIL_SELECT} WHERE c.id = ?`, [result.insertId])
@@ -240,20 +228,17 @@ async function update(req, res) {
const { id } = req.params
const fields = [
'customer_id', 'supplier_id', 'contract_name', 'contract_no', 'contract_content',
'amount', 'effective_date', 'expiry_date', 'employee_id', 'status', 'remark', 'type', 'responsible_user_id',
'amount', 'effective_date', 'expiry_date', 'employee_id', 'status', 'remark', 'type',
'responsible_user_id',
]
try {
// 确认记录存在且在数据范围内detail/update/delete 不用表别名)
let checkSql = 'SELECT id FROM contracts WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'contracts')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkSql = 'SELECT id, employee_id FROM contracts WHERE id = ?'
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {
@@ -273,6 +258,22 @@ async function update(req, res) {
return res.status(400).json({ code: 400, message: '关联业务员不存在' })
}
}
// 业务员即负责人:当 employee_id 变更时自动推导 responsible_user_id
if (req.body.employee_id !== undefined) {
if (req.body.employee_id) {
const [userRows] = await pool.query('SELECT id FROM users WHERE employee_id = ? LIMIT 1', [req.body.employee_id])
if (userRows.length > 0) {
req.body.responsible_user_id = userRows[0].id
} else {
// 该员工无对应系统用户时,设为当前登录用户
req.body.responsible_user_id = req.user.id
}
} else {
// 业务员被清空时,清空负责人
req.body.responsible_user_id = null
}
}
if (req.body.supplier_id) {
const [sup] = await pool.query('SELECT id FROM suppliers WHERE id = ?', [req.body.supplier_id])
if (sup.length === 0) {
@@ -313,14 +314,10 @@ async function remove(req, res) {
const { id } = req.params
try {
let checkSql = 'SELECT id FROM contracts WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'contracts')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {

View File

@@ -1,6 +1,5 @@
// routes/customers.js —— 客户管理 CRUD
// routes/customers.js —— 客户管理 CRUD(纯数据库操作,权限由中间件层控制)
const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions')
// 提取分页参数
function pagination(query) {
@@ -19,14 +18,10 @@ async function list(req, res) {
let where = 'WHERE 1=1'
const params = []
// 数据范围过滤
const scope = getDataScope(req.user, 'customers')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
where += ' AND ' + scope.where
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope
if (req.scope && req.scope.sql) {
where += ' ' + req.scope.sql
params.push(...req.scope.params)
}
if (id) {
@@ -56,12 +51,10 @@ async function list(req, res) {
params
)
// 查分页数据JOIN users+employees 获取负责人姓名)
// 查分页数据
const [rows] = await pool.query(
`SELECT c.*, e.name AS responsible_user_name
`SELECT c.*
FROM customers c
LEFT JOIN users u ON c.responsible_user_id = u.id
LEFT JOIN employees e ON u.employee_id = e.id
${where} ORDER BY c.id DESC LIMIT ? OFFSET ?`,
[...params, pageSize, offset]
)
@@ -89,13 +82,9 @@ async function detail(req, res) {
let sql = 'SELECT * FROM customers WHERE id = ?'
const params = [req.params.id]
const scope = getDataScope(req.user, 'customers')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
sql += ' AND ' + scope.where
params.push(...scope.values)
if (req.scope && req.scope.sql) {
sql += ' ' + req.scope.sql
params.push(...req.scope.params)
}
const [rows] = await pool.query(sql, params)
@@ -113,7 +102,7 @@ async function detail(req, res) {
async function create(req, res) {
const {
name, phone, province, city, district,
address, email, remark, responsible_user_id,
address, email, remark,
} = req.body || {}
if (!name) {
@@ -121,14 +110,11 @@ async function create(req, res) {
}
try {
// 如果没指定负责人,默认设为当前用户
const ownerId = responsible_user_id || req.user.id
const [result] = await pool.query(
`INSERT INTO customers (name, phone, province, city, district, address, email, remark, responsible_user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
`INSERT INTO customers (name, phone, province, city, district, address, email, remark)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[name, phone || null, province || null, city || null, district || null,
address || null, email || null, remark || null, ownerId]
address || null, email || null, remark || null]
)
const [rows] = await pool.query('SELECT * FROM customers WHERE id = ?', [result.insertId])
res.json({ code: 0, message: 'ok', data: rows[0] })
@@ -143,20 +129,16 @@ async function update(req, res) {
const { id } = req.params
const fields = [
'name', 'phone', 'province', 'city', 'district',
'address', 'email', 'remark', 'responsible_user_id',
'address', 'email', 'remark',
]
try {
// 确认记录存在且在数据范围内
let checkSql = 'SELECT id FROM customers WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'customers')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkParams = [id] // let, 后续可能 push scope.params
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {
@@ -192,14 +174,10 @@ async function remove(req, res) {
const { id } = req.params
try {
let checkSql = 'SELECT id FROM customers WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'customers')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {
@@ -216,4 +194,17 @@ async function remove(req, res) {
}
}
module.exports = { list, detail, create, update, remove }
// GET /api/customers/simple —— 简易加盟商列表(无数据范围限制,用于下拉选择)
async function simpleList(req, res) {
try {
const [rows] = await pool.query(
'SELECT id, name FROM customers ORDER BY id'
)
res.json({ code: 0, message: 'ok', data: rows })
} catch (e) {
console.error('[customers simpleList] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
module.exports = { list, detail, create, update, remove, simpleList }

View File

@@ -1,6 +1,6 @@
// routes/employees.js —— 员工管理 CRUD
// routes/employees.js —— 员工管理 CRUD(纯数据库操作,权限由中间件层控制)
const { pool } = require('../db')
const { getDataScope } = require('../middleware/permissions')
const bcrypt = require('bcryptjs')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
@@ -36,14 +36,10 @@ async function list(req, res) {
let where = 'WHERE 1=1'
const params = []
// 数据范围过滤(部门隔离
const scope = getDataScope(req.user, 'employees')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
where += ' AND ' + scope.where
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope
if (req.scope && req.scope.sql) {
where += ' ' + req.scope.sql
params.push(...req.scope.params)
}
if (id) {
@@ -96,13 +92,10 @@ async function detail(req, res) {
let sql = 'SELECT * FROM employees WHERE id = ?'
const params = [req.params.id]
const scope = getDataScope(req.user, 'employees')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
sql += ' AND ' + scope.where
params.push(...scope.values)
// 数据范围过滤(由中间件注入 req.scope
if (req.scope && req.scope.sql) {
sql += ' ' + req.scope.sql
params.push(...req.scope.params)
}
const [rows] = await pool.query(sql, params)
@@ -145,6 +138,18 @@ async function create(req, res) {
remark || null]
)
const [rows] = await pool.query('SELECT * FROM employees WHERE id = ?', [result.insertId])
// 如果中间件 allowUserCreation 要求同步创建用户账号
if (req._createUser) {
const { username, password, department_id } = req._createUser
const hash = await bcrypt.hash(password, 10)
await pool.query(
'INSERT INTO users (username, password, is_active, department_id, employee_id) VALUES (?, ?, 1, ?, ?)',
[username, hash, department_id, result.insertId]
)
delete req._createUser // 清理,避免影响后续中间件
}
res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) {
console.error('[employees create] error:', e)
@@ -163,14 +168,10 @@ async function update(req, res) {
try {
// 确认记录存在且在数据范围内
let checkSql = 'SELECT id FROM employees WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'employees')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {
@@ -197,11 +198,6 @@ async function update(req, res) {
params.push(id)
await pool.query(`UPDATE employees SET ${sets.join(', ')} WHERE id = ?`, params)
// 同步 department 到关联的 users 表
if (req.body.department !== undefined) {
await pool.query('UPDATE users SET department = ? WHERE employee_id = ?', [req.body.department, id])
}
const [rows] = await pool.query('SELECT * FROM employees WHERE id = ?', [id])
res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) {
@@ -215,14 +211,10 @@ async function remove(req, res) {
const { id } = req.params
try {
let checkSql = 'SELECT id FROM employees WHERE id = ?'
const checkParams = [id]
const scope = getDataScope(req.user, 'employees')
if (scope.deny) {
return res.status(403).json({ code: 403, message: '无权访问此资源' })
}
if (scope.where) {
checkSql += ' AND ' + scope.where
checkParams.push(...scope.values)
let checkParams = [id]
if (req.scope && req.scope.sql) {
checkSql += ' ' + req.scope.sql
checkParams.push(...req.scope.params)
}
const [existing] = await pool.query(checkSql, checkParams)
if (existing.length === 0) {

View File

@@ -151,4 +151,17 @@ async function remove(req, res) {
}
}
module.exports = { list, detail, create, update, remove }
// GET /api/suppliers/simple —— 简易供应商列表(无数据范围限制,用于下拉选择)
async function simpleList(req, res) {
try {
const [rows] = await pool.query(
'SELECT id, name FROM suppliers WHERE status = 1 ORDER BY id'
)
res.json({ code: 0, message: 'ok', data: rows })
} catch (e) {
console.error('[suppliers simpleList] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
module.exports = { list, detail, create, update, remove, simpleList }

View File

@@ -3,14 +3,14 @@ const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const { pool } = require('../db')
// 安全字段:关联查询 roles 和 employees
// 安全字段:关联查询 departments 和 employees
const USER_LIST_SQL = `
SELECT u.id, u.username, u.is_active, u.role_id, u.employee_id, u.department,
SELECT u.id, u.username, u.is_active, u.department_id, u.employee_id,
u.created_at, u.updated_at,
r.name AS role_name, r.description AS role_description,
d.name AS dept_name, d.description AS dept_desc,
e.name AS real_name
FROM users u
LEFT JOIN roles r ON u.role_id = r.id
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN employees e ON u.employee_id = e.id
`
@@ -32,10 +32,10 @@ async function login(req, res) {
try {
const [rows] = await pool.query(
`SELECT u.id, u.username, u.password, u.is_active, u.role_id, u.department, u.employee_id,
r.name AS role_name, e.name AS real_name
`SELECT u.id, u.username, u.password, u.is_active, u.department_id, u.employee_id,
d.name AS dept_name, d.description AS dept_desc, e.name AS real_name
FROM users u
LEFT JOIN roles r ON u.role_id = r.id
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN employees e ON u.employee_id = e.id
WHERE u.username = ?`,
[username]
@@ -56,12 +56,12 @@ async function login(req, res) {
return res.status(400).json({ code: 400, message: '账号或密码错误' })
}
// 查询该角色的所有权限标识
// 查询该部门的所有权限标识
const [perms] = await pool.query(
`SELECT p.name FROM permissions p
JOIN role_permissions rp ON p.id = rp.permission_id
WHERE rp.role_id = ?`,
[user.role_id]
JOIN department_permissions dp ON p.id = dp.permission_id
WHERE dp.department_id = ?`,
[user.department_id]
)
const permissions = perms.map(p => p.name)
@@ -70,9 +70,9 @@ async function login(req, res) {
id: user.id,
username: user.username,
name: user.real_name || user.username,
role_id: user.role_id,
roleName: user.role_name,
department: user.department,
department_id: user.department_id,
departmentName: user.dept_name,
departmentDesc: user.dept_desc,
permissions,
},
process.env.JWT_SECRET,
@@ -88,9 +88,9 @@ async function login(req, res) {
id: user.id,
username: user.username,
name: user.real_name || user.username,
role_id: user.role_id,
roleName: user.role_name,
department: user.department,
department_id: user.department_id,
departmentName: user.dept_name,
departmentDesc: user.dept_desc,
permissions,
},
},
@@ -177,7 +177,7 @@ async function changePassword(req, res) {
async function list(req, res) {
try {
const { page, pageSize, offset } = pagination(req.query)
const { username, real_name, id, role_id, is_active } = req.query
const { username, real_name, id, department_id, is_active } = req.query
let where = 'WHERE 1=1'
const params = []
@@ -194,9 +194,9 @@ async function list(req, res) {
where += ' AND u.id = ?'
params.push(Number(id))
}
if (role_id) {
where += ' AND u.role_id = ?'
params.push(Number(role_id))
if (department_id) {
where += ' AND u.department_id = ?'
params.push(Number(department_id))
}
if (is_active !== undefined && is_active !== '') {
where += ' AND u.is_active = ?'
@@ -251,7 +251,7 @@ async function detail(req, res) {
// POST /api/users —— 创建用户
async function create(req, res) {
const { username, password, role_id, employee_id, department, is_active = 1 } = req.body || {}
const { username, password, department_id, employee_id, is_active = 1 } = req.body || {}
if (!username || !password) {
return res.status(400).json({ code: 400, message: '用户名和密码必填' })
@@ -263,31 +263,27 @@ async function create(req, res) {
return res.status(400).json({ code: 400, message: '密码至少 6 位' })
}
// 校验 role_id 是否存在
if (role_id) {
const [role] = await pool.query('SELECT id FROM roles WHERE id = ?', [role_id])
if (role.length === 0) {
return res.status(400).json({ code: 400, message: '指定的角色不存在' })
// 校验 department_id 是否存在
if (department_id) {
const [dept] = await pool.query('SELECT id FROM departments WHERE id = ?', [department_id])
if (dept.length === 0) {
return res.status(400).json({ code: 400, message: '指定的部门不存在' })
}
}
// 校验 employee_id 是否存在
if (employee_id) {
const [emp] = await pool.query('SELECT id, department FROM employees WHERE id = ?', [employee_id])
const [emp] = await pool.query('SELECT id FROM employees WHERE id = ?', [employee_id])
if (emp.length === 0) {
return res.status(400).json({ code: 400, message: '指定的员工不存在' })
}
// 如果没传 department自动从员工表同步
if (!department) {
req.body.department = emp[0].department
}
}
try {
const hash = await bcrypt.hash(password, 10)
const [result] = await pool.query(
'INSERT INTO users (username, password, is_active, role_id, employee_id, department) VALUES (?, ?, ?, ?, ?, ?)',
[username, hash, is_active, role_id || null, employee_id || null, req.body.department || department || null]
'INSERT INTO users (username, password, is_active, department_id, employee_id) VALUES (?, ?, ?, ?, ?)',
[username, hash, is_active, department_id || null, employee_id || null]
)
const [rows] = await pool.query(
@@ -307,42 +303,30 @@ async function create(req, res) {
// PUT /api/users/:id —— 更新用户
async function update(req, res) {
const { id } = req.params
const fields = ['username', 'role_id', 'employee_id', 'department', 'is_active']
const fields = ['username', 'department_id', 'employee_id', 'is_active']
try {
const [existing] = await pool.query('SELECT id, role_id, is_active FROM users WHERE id = ?', [id])
const [existing] = await pool.query('SELECT id, department_id, is_active FROM users WHERE id = ?', [id])
if (existing.length === 0) {
return res.status(404).json({ code: 404, message: '用户不存在' })
}
// 不允许修改自己的角色或禁用自己
if (Number(id) === req.user.id) {
if (req.body.role_id !== undefined && req.body.role_id !== req.user.role_id) {
return res.status(400).json({ code: 400, message: '不能修改自己的角色' })
}
if (req.body.is_active === 0) {
return res.status(400).json({ code: 400, message: '不能禁用自己' })
}
}
// 不允许修改自己的部门或禁用自己(此检查已移至中间件 protectSelfUpdate
// 校验 role_id
if (req.body.role_id) {
const [role] = await pool.query('SELECT id FROM roles WHERE id = ?', [req.body.role_id])
if (role.length === 0) {
return res.status(400).json({ code: 400, message: '指定的角色不存在' })
// 校验 department_id
if (req.body.department_id) {
const [dept] = await pool.query('SELECT id FROM departments WHERE id = ?', [req.body.department_id])
if (dept.length === 0) {
return res.status(400).json({ code: 400, message: '指定的部门不存在' })
}
}
// 校验 employee_id
if (req.body.employee_id) {
const [emp] = await pool.query('SELECT id, department FROM employees WHERE id = ?', [req.body.employee_id])
const [emp] = await pool.query('SELECT id FROM employees WHERE id = ?', [req.body.employee_id])
if (emp.length === 0) {
return res.status(400).json({ code: 400, message: '指定的员工不存在' })
}
// 自动同步 department
if (req.body.department === undefined) {
req.body.department = emp[0].department
}
}
const sets = []
@@ -390,24 +374,12 @@ async function update(req, res) {
async function remove(req, res) {
const { id } = req.params
try {
if (Number(id) === req.user.id) {
return res.status(400).json({ code: 400, message: '不能删除自己' })
}
const [existing] = await pool.query('SELECT id, role_id FROM users WHERE id = ?', [id])
// 不能删除自己/最后一个管理员(此检查已移至中间件 protectUserDelete
const [existing] = await pool.query('SELECT id FROM users WHERE id = ?', [id])
if (existing.length === 0) {
return res.status(404).json({ code: 404, message: '用户不存在' })
}
// 禁止删除最后一个管理员
const [adminRole] = await pool.query('SELECT id FROM roles WHERE name = ?', ['admin'])
if (adminRole.length > 0 && existing[0].role_id === adminRole[0].id) {
const [[{ cnt }]] = await pool.query('SELECT COUNT(*) AS cnt FROM users WHERE role_id = ?', [adminRole[0].id])
if (cnt <= 1) {
return res.status(400).json({ code: 400, message: '不能删除最后一个管理员' })
}
}
await pool.query('DELETE FROM users WHERE id = ?', [id])
res.json({ code: 0, message: 'ok' })
} catch (e) {