Files
2026-04-19 14:05:40 +08:00

205 lines
7.9 KiB
JavaScript

const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const db = require('../db');
const { verifyToken, isSuperAdmin, isSystemAdmin } = require('../middleware/auth');
// 搜索用户 (所有登录用户可用,用于成果录入选择作者)
router.get('/search', verifyToken, async (req, res) => {
const { keyword, leaderOnly } = req.query;
if (!keyword) return res.json([]);
try {
let query = `
SELECT id, username as phone, real_name, department
FROM users
WHERE (username ILIKE $1 OR real_name ILIKE $1)
`;
const params = [`%${keyword}%`];
if (leaderOnly === 'true') {
// 获取所有领导层部门
const leaderDeptsRes = await db.query('SELECT name FROM dict_leader_departments');
const leaderDepts = leaderDeptsRes.rows.map(r => r.name);
if (leaderDepts.length > 0) {
query += ` AND department = ANY($${params.length + 1})`;
params.push(leaderDepts);
} else {
// 如果没有配置领导层部门,则返回空
return res.json([]);
}
}
query += ` LIMIT 20`;
const result = await db.query(query, params);
res.json(result.rows);
} catch (err) {
console.error(err);
res.status(500).json({ message: '服务器错误' });
}
});
// 获取所有待定状态用户的手机号列表 (用于前端标红显示)
router.get('/pending-list', verifyToken, async (req, res) => {
try {
const result = await db.query('SELECT username FROM users WHERE dept_status = \'pending\'');
const phones = result.rows.map(row => row.username);
res.json(phones);
} catch (err) {
res.status(500).json({ message: '服务器错误' });
}
});
// 获取所有用户列表 (系统管理员:超管或维护员)
router.get('/', verifyToken, isSystemAdmin, async (req, res) => {
const { keyword } = req.query;
try {
let query = 'SELECT id, username, role, real_name, department, dept_status, created_at FROM users WHERE 1=1';
const params = [];
if (keyword) {
query += ` AND (username ILIKE $1 OR real_name ILIKE $1 OR department ILIKE $1)`;
params.push(`%${keyword}%`);
}
query += ' ORDER BY created_at DESC';
const result = await db.query(query, params);
res.json(result.rows);
} catch (err) {
res.status(500).json({ message: '服务器错误' });
}
});
// 创建新用户 (仅超管)
router.post('/', verifyToken, isSuperAdmin, async (req, res) => {
const { username, password, role, real_name, department: inputDepartment } = req.body;
let department = inputDepartment;
if (!username || !password || !role) {
return res.status(400).json({ message: '缺少必填字段 (username, password, role)' });
}
try {
// 手机号格式校验
if (!/^1[3-9]\d{9}$/.test(username)) {
return res.status(400).json({ message: '手机号格式不正确' });
}
const userExists = await db.query('SELECT * FROM users WHERE username = $1', [username]);
if (userExists.rows.length > 0) {
return res.status(400).json({ message: '该手机号已注册' });
}
// 校验部门
let deptStatus = 'verified';
if (['admin', 'user'].includes(role)) {
if (!department) {
return res.status(400).json({ message: '该角色必须指定部门' });
}
const deptCheck = await db.query('SELECT * FROM departments WHERE name = $1', [department]);
deptStatus = deptCheck.rows.length > 0 ? 'verified' : 'pending';
} else {
// super_admin 或 maintainer 可以没有部门
if (!department) department = null;
}
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(password, salt);
const result = await db.query(
'INSERT INTO users (username, password_hash, role, real_name, department, dept_status) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, username, role, real_name, department, dept_status',
[username, passwordHash, role, real_name, department, deptStatus]
);
res.status(201).json(result.rows[0]);
} catch (err) {
res.status(500).json({ message: '服务器错误' });
}
});
// 修改用户信息 (系统管理员:超管或维护员)
router.put('/:id', verifyToken, isSystemAdmin, async (req, res) => {
const { id } = req.params;
let { username, role, real_name, department, password } = req.body;
// 如果是维护员,只能修改密码,忽略其他字段
if (req.user.role === 'maintainer') {
if (!password) {
return res.json({ message: '未提供新密码,无任何修改' });
}
try {
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(password, salt);
await db.query(
'UPDATE users SET password_hash=$1, token_version = CASE WHEN token_version >= 2000000000 THEN 1 ELSE token_version + 1 END WHERE id=$2',
[passwordHash, id]
);
return res.json({ message: '密码重置成功' });
} catch (err) {
return res.status(500).json({ message: '服务器错误' });
}
}
// 以下逻辑仅适用于超级管理员 (super_admin)
try {
// 手机号格式校验
if (!/^1[3-9]\d{9}$/.test(username)) {
return res.status(400).json({ message: '手机号格式不正确' });
}
// 校验部门
let deptStatus = 'verified';
if (['admin', 'user'].includes(role)) {
if (!department) {
return res.status(400).json({ message: '该角色必须指定部门' });
}
const deptCheck = await db.query('SELECT * FROM departments WHERE name = $1', [department]);
deptStatus = deptCheck.rows.length > 0 ? 'verified' : 'pending';
} else {
// super_admin 或 maintainer 可以没有部门
if (!department) department = null;
}
let query = 'UPDATE users SET username=$1, role=$2, real_name=$3, department=$4, dept_status=$5';
let params = [username, role, real_name, department, deptStatus, id];
if (password) {
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(password, salt);
query += ', password_hash=$6, token_version = CASE WHEN token_version >= 2000000000 THEN 1 ELSE token_version + 1 END WHERE id=$7';
params = [username, role, real_name, department, deptStatus, passwordHash, id];
} else {
query += ' WHERE id=$6';
}
await db.query(query, params);
res.json({ message: '修改成功' });
} catch (err) {
res.status(500).json({ message: '服务器错误' });
}
});
// 删除用户 (仅超管)
router.delete('/:id', verifyToken, isSuperAdmin, async (req, res) => {
const { id } = req.params;
try {
// 防止删除自己
if (parseInt(id) === req.user.id) {
return res.status(400).json({ message: '不能删除当前登录账号' });
}
// 检查是否有关联的成果
const achievementCheck = await db.query('SELECT id FROM achievements WHERE user_id = $1 LIMIT 1', [id]);
if (achievementCheck.rows.length > 0) {
return res.status(400).json({ message: '该用户有关联的成果数据,无法直接删除。请先处理关联数据。' });
}
await db.query('DELETE FROM users WHERE id = $1', [id]);
res.json({ message: '删除成功' });
} catch (err) {
console.error(err);
res.status(500).json({ message: '服务器错误' });
}
});
module.exports = router;