重构后端,使其更加权责分明
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
110
routes/users.js
110
routes/users.js
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user