重构后端服务架构并优化前端错误处理
This commit is contained in:
@@ -3,39 +3,9 @@ const bcrypt = require('bcryptjs')
|
||||
const Joi = require('joi')
|
||||
const router = express.Router()
|
||||
|
||||
// 模拟用户数据
|
||||
let users = [
|
||||
{
|
||||
id: 1,
|
||||
username: 'admin',
|
||||
email: 'admin@example.com',
|
||||
phone: '13800138000',
|
||||
role: 'admin',
|
||||
status: 'active',
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
updatedAt: '2024-01-01T00:00:00Z'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: 'buyer01',
|
||||
email: 'buyer01@example.com',
|
||||
phone: '13800138001',
|
||||
role: 'buyer',
|
||||
status: 'active',
|
||||
createdAt: '2024-01-02T00:00:00Z',
|
||||
updatedAt: '2024-01-02T00:00:00Z'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
username: 'supplier01',
|
||||
email: 'supplier01@example.com',
|
||||
phone: '13800138002',
|
||||
role: 'supplier',
|
||||
status: 'inactive',
|
||||
createdAt: '2024-01-03T00:00:00Z',
|
||||
updatedAt: '2024-01-03T00:00:00Z'
|
||||
}
|
||||
]
|
||||
// 引入数据库模型
|
||||
const { ApiUser } = require('../models')
|
||||
const sequelize = require('sequelize')
|
||||
|
||||
// 验证模式
|
||||
const createUserSchema = Joi.object({
|
||||
@@ -43,60 +13,55 @@ const createUserSchema = Joi.object({
|
||||
email: Joi.string().email().required(),
|
||||
phone: Joi.string().pattern(/^1[3-9]\d{9}$/).allow(''),
|
||||
password: Joi.string().min(6).max(100).required(),
|
||||
role: Joi.string().valid('admin', 'buyer', 'trader', 'supplier', 'driver').required(),
|
||||
status: Joi.string().valid('active', 'inactive').default('active')
|
||||
user_type: Joi.string().valid('client', 'supplier', 'driver', 'staff', 'admin').required(),
|
||||
status: Joi.string().valid('active', 'inactive', 'locked').default('active')
|
||||
})
|
||||
|
||||
const updateUserSchema = Joi.object({
|
||||
username: Joi.string().min(2).max(50),
|
||||
email: Joi.string().email(),
|
||||
phone: Joi.string().pattern(/^1[3-9]\d{9}$/).allow(''),
|
||||
role: Joi.string().valid('admin', 'buyer', 'trader', 'supplier', 'driver'),
|
||||
status: Joi.string().valid('active', 'inactive', 'banned')
|
||||
user_type: Joi.string().valid('client', 'supplier', 'driver', 'staff', 'admin'),
|
||||
status: Joi.string().valid('active', 'inactive', 'locked')
|
||||
})
|
||||
|
||||
// 获取用户列表
|
||||
router.get('/', (req, res) => {
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const { page = 1, pageSize = 20, keyword, role, status } = req.query
|
||||
const { page = 1, pageSize = 20, keyword, user_type, status } = req.query
|
||||
|
||||
let filteredUsers = [...users]
|
||||
|
||||
// 关键词搜索
|
||||
// 构建查询条件
|
||||
const where = {}
|
||||
if (keyword) {
|
||||
filteredUsers = filteredUsers.filter(user =>
|
||||
user.username.includes(keyword) ||
|
||||
user.email.includes(keyword)
|
||||
)
|
||||
where[sequelize.Op.or] = [
|
||||
{ username: { [sequelize.Op.like]: `%${keyword}%` } },
|
||||
{ email: { [sequelize.Op.like]: `%${keyword}%` } },
|
||||
{ phone: { [sequelize.Op.like]: `%${keyword}%` } }
|
||||
]
|
||||
}
|
||||
if (user_type) where.user_type = user_type
|
||||
if (status) where.status = status
|
||||
|
||||
// 角色筛选
|
||||
if (role) {
|
||||
filteredUsers = filteredUsers.filter(user => user.role === role)
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (status) {
|
||||
filteredUsers = filteredUsers.filter(user => user.status === status)
|
||||
}
|
||||
|
||||
// 分页
|
||||
const total = filteredUsers.length
|
||||
const startIndex = (page - 1) * pageSize
|
||||
const endIndex = startIndex + parseInt(pageSize)
|
||||
const paginatedUsers = filteredUsers.slice(startIndex, endIndex)
|
||||
// 分页查询
|
||||
const result = await ApiUser.findAndCountAll({
|
||||
where,
|
||||
limit: parseInt(pageSize),
|
||||
offset: (parseInt(page) - 1) * parseInt(pageSize),
|
||||
order: [['createdAt', 'DESC']]
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
items: paginatedUsers,
|
||||
total: total,
|
||||
items: result.rows,
|
||||
total: result.count,
|
||||
page: parseInt(page),
|
||||
pageSize: parseInt(pageSize),
|
||||
totalPages: Math.ceil(total / pageSize)
|
||||
totalPages: Math.ceil(result.count / parseInt(pageSize))
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取用户列表失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取用户列表失败'
|
||||
@@ -105,10 +70,10 @@ router.get('/', (req, res) => {
|
||||
})
|
||||
|
||||
// 获取用户详情
|
||||
router.get('/:id', (req, res) => {
|
||||
router.get('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const user = users.find(u => u.id === parseInt(id))
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
@@ -122,6 +87,7 @@ router.get('/:id', (req, res) => {
|
||||
data: user
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取用户详情失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取用户详情失败'
|
||||
@@ -142,37 +108,39 @@ router.post('/', async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
const { username, email, phone, password, role, status } = value
|
||||
const { username, email, phone, password, user_type, status } = value
|
||||
|
||||
// 检查用户名是否已存在
|
||||
if (users.find(u => u.username === username)) {
|
||||
const existingUser = await ApiUser.findOne({
|
||||
where: {
|
||||
[sequelize.Op.or]: [
|
||||
{ username: username },
|
||||
{ email: email },
|
||||
{ phone: phone }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
if (existingUser) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '用户名已存在'
|
||||
message: '用户名、邮箱或手机号已存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if (users.find(u => u.email === email)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '邮箱已存在'
|
||||
})
|
||||
}
|
||||
// 密码加密
|
||||
const saltRounds = 10
|
||||
const password_hash = await bcrypt.hash(password, saltRounds)
|
||||
|
||||
// 创建新用户
|
||||
const newUser = {
|
||||
id: Math.max(...users.map(u => u.id)) + 1,
|
||||
const newUser = await ApiUser.create({
|
||||
username,
|
||||
email,
|
||||
phone: phone || '',
|
||||
role,
|
||||
password_hash,
|
||||
user_type,
|
||||
status,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
}
|
||||
|
||||
users.push(newUser)
|
||||
})
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
@@ -180,6 +148,7 @@ router.post('/', async (req, res) => {
|
||||
data: newUser
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('创建用户失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '创建用户失败'
|
||||
@@ -188,12 +157,12 @@ router.post('/', async (req, res) => {
|
||||
})
|
||||
|
||||
// 更新用户
|
||||
router.put('/:id', (req, res) => {
|
||||
router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const userIndex = users.findIndex(u => u.id === parseInt(id))
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (userIndex === -1) {
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
@@ -211,18 +180,15 @@ router.put('/:id', (req, res) => {
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
users[userIndex] = {
|
||||
...users[userIndex],
|
||||
...value,
|
||||
updatedAt: new Date().toISOString()
|
||||
}
|
||||
await user.update(value)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '用户更新成功',
|
||||
data: users[userIndex]
|
||||
data: user
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('更新用户失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '更新用户失败'
|
||||
@@ -231,25 +197,26 @@ router.put('/:id', (req, res) => {
|
||||
})
|
||||
|
||||
// 删除用户
|
||||
router.delete('/:id', (req, res) => {
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const userIndex = users.findIndex(u => u.id === parseInt(id))
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (userIndex === -1) {
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
users.splice(userIndex, 1)
|
||||
await user.destroy()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '用户删除成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('删除用户失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '删除用户失败'
|
||||
@@ -258,7 +225,7 @@ router.delete('/:id', (req, res) => {
|
||||
})
|
||||
|
||||
// 批量删除用户
|
||||
router.delete('/batch', (req, res) => {
|
||||
router.delete('/batch', async (req, res) => {
|
||||
try {
|
||||
const { ids } = req.body
|
||||
|
||||
@@ -269,13 +236,18 @@ router.delete('/batch', (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
users = users.filter(user => !ids.includes(user.id))
|
||||
await ApiUser.destroy({
|
||||
where: {
|
||||
id: ids
|
||||
}
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `成功删除 ${ids.length} 个用户`
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('批量删除用户失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量删除用户失败'
|
||||
@@ -289,8 +261,8 @@ router.put('/:id/password', async (req, res) => {
|
||||
const { id } = req.params
|
||||
const { password } = req.body
|
||||
|
||||
const userIndex = users.findIndex(u => u.id === parseInt(id))
|
||||
if (userIndex === -1) {
|
||||
const user = await ApiUser.findByPk(id)
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
@@ -304,14 +276,19 @@ router.put('/:id/password', async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 在实际项目中,这里会对密码进行加密
|
||||
users[userIndex].updatedAt = new Date().toISOString()
|
||||
// 密码加密
|
||||
const saltRounds = 10
|
||||
const password_hash = await bcrypt.hash(password, saltRounds)
|
||||
|
||||
// 更新密码
|
||||
await user.update({ password_hash })
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '密码重置成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('重置密码失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '重置密码失败'
|
||||
@@ -320,35 +297,35 @@ router.put('/:id/password', async (req, res) => {
|
||||
})
|
||||
|
||||
// 更新用户状态
|
||||
router.put('/:id/status', (req, res) => {
|
||||
router.put('/:id/status', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { status } = req.body
|
||||
|
||||
const userIndex = users.findIndex(u => u.id === parseInt(id))
|
||||
if (userIndex === -1) {
|
||||
const user = await ApiUser.findByPk(id)
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
if (!['active', 'inactive', 'banned'].includes(status)) {
|
||||
if (!['active', 'inactive', 'locked'].includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的用户状态'
|
||||
})
|
||||
}
|
||||
|
||||
users[userIndex].status = status
|
||||
users[userIndex].updatedAt = new Date().toISOString()
|
||||
await user.update({ status })
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '用户状态更新成功',
|
||||
data: users[userIndex]
|
||||
data: user
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('更新用户状态失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '更新用户状态失败'
|
||||
|
||||
Reference in New Issue
Block a user