Merge remote-tracking branch 'origin/main'

This commit is contained in:
2025-09-12 13:15:03 +08:00
committed by aiotagro
28 changed files with 10237 additions and 1945 deletions

View File

@@ -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