Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -7,24 +7,176 @@ const router = express.Router()
|
||||
const { ApiUser } = require('../models')
|
||||
const sequelize = require('sequelize')
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* User:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* description: 用户ID
|
||||
* username:
|
||||
* type: string
|
||||
* description: 用户名
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* description: 邮箱
|
||||
* phone:
|
||||
* type: string
|
||||
* description: 手机号
|
||||
* user_type:
|
||||
* type: string
|
||||
* enum: [admin, buyer, supplier, trader]
|
||||
* description: 用户类型
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [active, inactive, suspended]
|
||||
* description: 用户状态
|
||||
* createdAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: 创建时间
|
||||
* updatedAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: 更新时间
|
||||
* CreateUserRequest:
|
||||
* type: object
|
||||
* required:
|
||||
* - username
|
||||
* - email
|
||||
* - password
|
||||
* - user_type
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* description: 用户名
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* description: 邮箱
|
||||
* phone:
|
||||
* type: string
|
||||
* description: 手机号
|
||||
* password:
|
||||
* type: string
|
||||
* description: 密码
|
||||
* user_type:
|
||||
* type: string
|
||||
* enum: [admin, buyer, supplier, trader]
|
||||
* description: 用户类型
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [active, inactive, suspended]
|
||||
* description: 用户状态
|
||||
* UpdateUserRequest:
|
||||
* type: object
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* description: 用户名
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* description: 邮箱
|
||||
* phone:
|
||||
* type: string
|
||||
* description: 手机号
|
||||
* user_type:
|
||||
* type: string
|
||||
* enum: [admin, buyer, supplier, trader]
|
||||
* description: 用户类型
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [active, inactive, suspended]
|
||||
* description: 用户状态
|
||||
*/
|
||||
|
||||
// 验证模式
|
||||
const createUserSchema = Joi.object({
|
||||
username: Joi.string().min(2).max(50).required(),
|
||||
email: Joi.string().email().required(),
|
||||
phone: Joi.string().pattern(/^1[3-9]\d{9}$/).allow(''),
|
||||
password: Joi.string().min(6).max(100).required(),
|
||||
user_type: Joi.string().valid('client', 'supplier', 'driver', 'staff', 'admin').required(),
|
||||
status: Joi.string().valid('active', 'inactive', 'locked').default('active')
|
||||
user_type: Joi.string().valid('admin', 'buyer', 'supplier', 'trader').required(),
|
||||
status: Joi.string().valid('active', 'inactive', 'suspended').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(''),
|
||||
user_type: Joi.string().valid('client', 'supplier', 'driver', 'staff', 'admin'),
|
||||
status: Joi.string().valid('active', 'inactive', 'locked')
|
||||
user_type: Joi.string().valid('admin', 'buyer', 'supplier', 'trader'),
|
||||
status: Joi.string().valid('active', 'inactive', 'suspended')
|
||||
})
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users:
|
||||
* get:
|
||||
* summary: 获取用户列表
|
||||
* tags: [用户管理]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 页码,默认为1
|
||||
* - in: query
|
||||
* name: pageSize
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 每页条数,默认为20
|
||||
* - in: query
|
||||
* name: keyword
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 关键词搜索(用户名、邮箱、手机号)
|
||||
* - in: query
|
||||
* name: user_type
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 用户类型筛选
|
||||
* - in: query
|
||||
* name: status
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 用户状态筛选
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* items:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* total:
|
||||
* type: integer
|
||||
* page:
|
||||
* type: integer
|
||||
* pageSize:
|
||||
* type: integer
|
||||
* totalPages:
|
||||
* type: integer
|
||||
* 401:
|
||||
* description: 未授权
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
*/
|
||||
// 获取用户列表
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
@@ -69,10 +221,45 @@ router.get('/', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/{id}:
|
||||
* get:
|
||||
* summary: 获取用户详情
|
||||
* tags: [用户管理]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: 用户ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* data:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* 401:
|
||||
* description: 未授权
|
||||
* 404:
|
||||
* description: 用户不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
*/
|
||||
// 获取用户详情
|
||||
router.get('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (!user) {
|
||||
@@ -95,10 +282,44 @@ router.get('/:id', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 创建用户
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users:
|
||||
* post:
|
||||
* summary: 创建新用户
|
||||
* tags: [用户管理]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/CreateUserRequest'
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 创建成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* message:
|
||||
* type: string
|
||||
* data:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* 400:
|
||||
* description: 参数验证失败
|
||||
* 401:
|
||||
* description: 未授权
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
*/
|
||||
// 创建新用户
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
// 参数验证
|
||||
const { error, value } = createUserSchema.validate(req.body)
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
@@ -110,13 +331,12 @@ router.post('/', async (req, res) => {
|
||||
|
||||
const { username, email, phone, password, user_type, status } = value
|
||||
|
||||
// 检查用户名是否已存在
|
||||
// 检查用户名、邮箱是否已存在
|
||||
const existingUser = await ApiUser.findOne({
|
||||
where: {
|
||||
[sequelize.Op.or]: [
|
||||
{ username: username },
|
||||
{ email: email },
|
||||
{ phone: phone }
|
||||
{ username },
|
||||
{ email }
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -124,28 +344,31 @@ router.post('/', async (req, res) => {
|
||||
if (existingUser) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '用户名、邮箱或手机号已存在'
|
||||
message: '用户名或邮箱已被使用'
|
||||
})
|
||||
}
|
||||
|
||||
// 密码加密
|
||||
const saltRounds = 10
|
||||
const password_hash = await bcrypt.hash(password, saltRounds)
|
||||
const hashedPassword = await bcrypt.hash(password, 10)
|
||||
|
||||
// 创建新用户
|
||||
const newUser = await ApiUser.create({
|
||||
// 创建用户
|
||||
const user = await ApiUser.create({
|
||||
username,
|
||||
email,
|
||||
phone: phone || '',
|
||||
password_hash,
|
||||
phone,
|
||||
password_hash: hashedPassword,
|
||||
user_type,
|
||||
status,
|
||||
status
|
||||
})
|
||||
|
||||
// 移除密码哈希,避免返回敏感信息
|
||||
const userData = user.toJSON()
|
||||
delete userData.password_hash
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '用户创建成功',
|
||||
data: newUser
|
||||
data: userData
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('创建用户失败:', error)
|
||||
@@ -156,20 +379,55 @@ router.post('/', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 更新用户
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/{id}:
|
||||
* put:
|
||||
* summary: 更新用户信息
|
||||
* tags: [用户管理]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: 用户ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/UpdateUserRequest'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 更新成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* message:
|
||||
* type: string
|
||||
* data:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* 400:
|
||||
* description: 参数验证失败
|
||||
* 401:
|
||||
* description: 未授权
|
||||
* 404:
|
||||
* description: 用户不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
*/
|
||||
// 更新用户信息
|
||||
router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 参数验证
|
||||
const { error, value } = updateUserSchema.validate(req.body)
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
@@ -179,6 +437,16 @@ router.put('/:id', async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
await user.update(value)
|
||||
|
||||
@@ -196,10 +464,45 @@ router.put('/:id', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/users/{id}:
|
||||
* delete:
|
||||
* summary: 删除用户
|
||||
* tags: [用户管理]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: 用户ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 删除成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* message:
|
||||
* type: string
|
||||
* 401:
|
||||
* description: 未授权
|
||||
* 404:
|
||||
* description: 用户不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
*/
|
||||
// 删除用户
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
|
||||
const user = await ApiUser.findByPk(id)
|
||||
|
||||
if (!user) {
|
||||
@@ -209,6 +512,8 @@ router.delete('/:id', async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 软删除或永久删除
|
||||
// 如果需要软删除,可以改为更新status为'inactive'
|
||||
await user.destroy()
|
||||
|
||||
res.json({
|
||||
@@ -224,113 +529,4 @@ router.delete('/:id', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 批量删除用户
|
||||
router.delete('/batch', async (req, res) => {
|
||||
try {
|
||||
const { ids } = req.body
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请提供有效的用户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: '批量删除用户失败'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 重置用户密码
|
||||
router.put('/:id/password', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { password } = req.body
|
||||
|
||||
const user = await ApiUser.findByPk(id)
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
if (!password || password.length < 6) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '密码长度不能少于6位'
|
||||
})
|
||||
}
|
||||
|
||||
// 密码加密
|
||||
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: '重置密码失败'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 更新用户状态
|
||||
router.put('/:id/status', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { status } = req.body
|
||||
|
||||
const user = await ApiUser.findByPk(id)
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
})
|
||||
}
|
||||
|
||||
if (!['active', 'inactive', 'locked'].includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '无效的用户状态'
|
||||
})
|
||||
}
|
||||
|
||||
await user.update({ status })
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '用户状态更新成功',
|
||||
data: user
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('更新用户状态失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '更新用户状态失败'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
Reference in New Issue
Block a user