Files
backmanager-server/routes/employees.js
2026-06-29 14:43:21 +08:00

269 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// routes/employees.js —— 员工管理 CRUD纯数据库操作权限由中间件层控制
const { pool } = require('../db')
const bcrypt = require('bcryptjs')
// 格式化日期为 YYYY-MM-DD
function formatDate(dateStr) {
if (!dateStr) return null
if (typeof dateStr === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
return dateStr // 已经是正确格式
}
try {
const date = new Date(dateStr)
if (isNaN(date.getTime())) return null
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch {
return null
}
}
function pagination(query) {
const page = Math.max(Number(query.page) || 1, 1)
const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100)
const offset = (page - 1) * pageSize
return { page, pageSize, offset }
}
// GET /api/employees —— 列表
async function list(req, res) {
try {
const { page, pageSize, offset } = pagination(req.query)
const { id, name, status, department } = req.query
let where = 'WHERE 1=1'
const params = []
// 数据范围过滤(由中间件注入 req.scope
if (req.scope && req.scope.sql) {
where += ' ' + req.scope.sql
params.push(...req.scope.params)
}
if (id) {
where += ' AND id = ?'
params.push(Number(id))
}
if (name) {
where += ' AND name LIKE ?'
params.push(`%${name}%`)
}
if (status !== undefined && status !== '') {
where += ' AND status = ?'
params.push(Number(status))
}
if (department) {
where += ' AND department LIKE ?'
params.push(`%${department}%`)
}
const [[{ total }]] = await pool.query(
`SELECT COUNT(*) AS total FROM employees ${where}`,
params
)
const [rows] = await pool.query(
`SELECT * FROM employees ${where} ORDER BY id ASC LIMIT ? OFFSET ?`,
[...params, pageSize, offset]
)
res.json({
code: 0,
message: 'ok',
data: {
list: rows,
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize),
},
})
} catch (e) {
console.error('[employees list] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// GET /api/employees/:id —— 详情
async function detail(req, res) {
try {
let sql = 'SELECT * FROM employees WHERE id = ?'
const params = [req.params.id]
// 数据范围过滤(由中间件注入 req.scope
if (req.scope && req.scope.sql) {
sql += ' ' + req.scope.sql
params.push(...req.scope.params)
}
const [rows] = await pool.query(sql, params)
if (rows.length === 0) {
return res.status(404).json({ code: 404, message: '员工不存在' })
}
res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) {
console.error('[employees detail] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// POST /api/employees —— 新增
async function create(req, res) {
const {
name, gender, age, education, department,
entry_date, position, salary, phone, email, status, remark,
} = req.body || {}
if (!name) {
return res.status(400).json({ code: 400, message: '员工姓名必填' })
}
try {
const [result] = await pool.query(
`INSERT INTO employees (name, gender, age, education, department, entry_date, position, salary, phone, email, status, remark)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[name,
gender || null,
age !== undefined ? age : null,
education || null,
department || null,
formatDate(entry_date),
position || null,
salary !== undefined ? salary : null,
phone || null,
email || null,
status !== undefined ? status : 1,
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)
res.status(500).json({ code: 500, message: e.message })
}
}
// PUT /api/employees/:id —— 更新
async function update(req, res) {
const { id } = req.params
const fields = [
'name', 'gender', 'age', 'education', 'department',
'entry_date', 'position', 'salary', 'phone', 'email', 'status', 'remark',
]
try {
// 确认记录存在且在数据范围内
let checkSql = 'SELECT id FROM employees 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) {
return res.status(404).json({ code: 404, message: '员工不存在或无权操作' })
}
const sets = []
const params = []
for (const f of fields) {
if (req.body[f] !== undefined) {
sets.push(`${f} = ?`)
// 日期字段需要格式化
if (f === 'entry_date') {
params.push(formatDate(req.body[f]))
} else {
params.push(req.body[f])
}
}
}
if (sets.length === 0) {
return res.status(400).json({ code: 400, message: '没有需要更新的字段' })
}
params.push(id)
await pool.query(`UPDATE employees SET ${sets.join(', ')} WHERE id = ?`, params)
// 部门联动:员工部门变更时同步更新关联用户的部门
if (req.body.department !== undefined) {
const [[dept]] = await pool.query('SELECT id FROM departments WHERE description = ?', [req.body.department])
if (dept) {
await pool.query('UPDATE users SET department_id = ? WHERE employee_id = ?', [dept.id, id])
}
}
const [rows] = await pool.query('SELECT * FROM employees WHERE id = ?', [id])
res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) {
console.error('[employees update] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// DELETE /api/employees/:id —— 删除
async function remove(req, res) {
const { id } = req.params
try {
let checkSql = 'SELECT id FROM employees 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) {
return res.status(404).json({ code: 404, message: '员工不存在或无权操作' })
}
// 禁止删除自己关联的员工
const [selfCheck] = await pool.query('SELECT id FROM users WHERE employee_id = ? AND id = ?', [id, req.user.id])
if (selfCheck.length > 0) {
return res.status(400).json({ code: 400, message: '不能删除自己的员工账号' })
}
// 同步删除关联的用户账号
await pool.query('DELETE FROM users WHERE employee_id = ?', [id])
await pool.query('DELETE FROM employees WHERE id = ?', [id])
res.json({ code: 0, message: 'ok' })
} catch (e) {
console.error('[employees delete] error:', e)
if (e.code === 'ER_ROW_IS_REFERENCED_2') {
return res.status(400).json({ code: 400, message: '该员工被合同或售后记录引用,无法删除' })
}
res.status(500).json({ code: 500, message: e.message })
}
}
// GET /api/employees/simple —— 简易员工列表(无数据范围限制,用于下拉选择)
async function simpleList(req, res) {
try {
const { department } = req.query
let sql = 'SELECT id, name, department, position FROM employees WHERE status = 1'
const params = []
if (department) {
sql += ' AND department = ?'
params.push(department)
}
sql += ' ORDER BY id'
const [rows] = await pool.query(sql, params)
res.json({ code: 0, message: 'ok', data: rows })
} catch (e) {
console.error('[employees simpleList] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
module.exports = { list, detail, create, update, remove, simpleList }