You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
13 KiB
414 lines
13 KiB
import UserModel from "db/models/UserModel.js"
|
|
import { hashPassword, comparePassword } from "utils/bcrypt.js"
|
|
import CommonError from "utils/error/CommonError.js"
|
|
import { JWT_SECRET } from "@/middlewares/Auth/auth.js"
|
|
import jwt from "@/middlewares/Auth/jwt.js"
|
|
|
|
class UserService {
|
|
// 根据ID获取用户
|
|
async getUserById(id) {
|
|
try {
|
|
if (!id) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
const user = await UserModel.findById(id)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
// 返回脱敏信息
|
|
const { password, ...userInfo } = user
|
|
return userInfo
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`获取用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 获取所有用户
|
|
async getAllUsers() {
|
|
try {
|
|
const users = await UserModel.findAll()
|
|
// 返回脱敏信息
|
|
return users.map(user => {
|
|
const { password, ...userInfo } = user
|
|
return userInfo
|
|
})
|
|
} catch (error) {
|
|
throw new CommonError(`获取用户列表失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 创建新用户
|
|
async createUser(data) {
|
|
try {
|
|
if (!data.username || !data.password) {
|
|
throw new CommonError("用户名和密码为必填字段")
|
|
}
|
|
|
|
// 检查用户名是否已存在
|
|
const existUser = await UserModel.findByUsername(data.username)
|
|
if (existUser) {
|
|
throw new CommonError(`用户名${data.username}已存在`)
|
|
}
|
|
|
|
// 检查邮箱是否已存在
|
|
if (data.email) {
|
|
const existEmail = await UserModel.findByEmail(data.email)
|
|
if (existEmail) {
|
|
throw new CommonError(`邮箱${data.email}已被使用`)
|
|
}
|
|
}
|
|
|
|
// 密码加密
|
|
const hashedPassword = await hashPassword(data.password)
|
|
|
|
const user = await UserModel.create({
|
|
...data,
|
|
password: hashedPassword
|
|
})
|
|
|
|
// 返回脱敏信息
|
|
const { password, ...userInfo } = Array.isArray(user) ? user[0] : user
|
|
return userInfo
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`创建用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 更新用户
|
|
async updateUser(id, data) {
|
|
try {
|
|
if (!id) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
|
|
const user = await UserModel.findById(id)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
// 如果要更新用户名,检查是否重复
|
|
if (data.username && data.username !== user.username) {
|
|
const existUser = await UserModel.findByUsername(data.username)
|
|
if (existUser) {
|
|
throw new CommonError(`用户名${data.username}已存在`)
|
|
}
|
|
}
|
|
|
|
// 如果要更新邮箱,检查是否重复
|
|
if (data.email && data.email !== user.email) {
|
|
const existEmail = await UserModel.findByEmail(data.email)
|
|
if (existEmail) {
|
|
throw new CommonError(`邮箱${data.email}已被使用`)
|
|
}
|
|
}
|
|
|
|
// 如果要更新密码,需要加密
|
|
if (data.password) {
|
|
data.password = await hashPassword(data.password)
|
|
}
|
|
|
|
const updatedUser = await UserModel.update(id, data)
|
|
|
|
// 返回脱敏信息
|
|
const { password, ...userInfo } = Array.isArray(updatedUser) ? updatedUser[0] : updatedUser
|
|
return userInfo
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`更新用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 删除用户
|
|
async deleteUser(id) {
|
|
try {
|
|
if (!id) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
|
|
const user = await UserModel.findById(id)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
return await UserModel.delete(id)
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`删除用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 注册新用户
|
|
async register(data) {
|
|
try {
|
|
if (!data.username || !data.password) {
|
|
throw new CommonError("用户名和密码不能为空")
|
|
}
|
|
|
|
// 检查用户名是否已存在
|
|
const existUser = await UserModel.findByUsername(data.username)
|
|
if (existUser) {
|
|
throw new CommonError(`用户名${data.username}已存在`)
|
|
}
|
|
|
|
// 检查邮箱是否已存在
|
|
if (data.email) {
|
|
const existEmail = await UserModel.findByEmail(data.email)
|
|
if (existEmail) {
|
|
throw new CommonError(`邮箱${data.email}已被使用`)
|
|
}
|
|
}
|
|
|
|
// 密码加密
|
|
const hashed = await hashPassword(data.password)
|
|
|
|
const user = await UserModel.create({ ...data, password: hashed })
|
|
|
|
// 返回脱敏信息
|
|
const { password, ...userInfo } = Array.isArray(user) ? user[0] : user
|
|
return userInfo
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`注册失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 登录
|
|
async login({ username, email, password }) {
|
|
try {
|
|
if (!password) {
|
|
throw new CommonError("密码不能为空")
|
|
}
|
|
|
|
if (!username && !email) {
|
|
throw new CommonError("用户名或邮箱不能为空")
|
|
}
|
|
|
|
let user
|
|
if (username) {
|
|
user = await UserModel.findByUsername(username)
|
|
} else if (email) {
|
|
user = await UserModel.findByEmail(email)
|
|
}
|
|
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
// 校验密码
|
|
const ok = await comparePassword(password, user.password)
|
|
if (!ok) {
|
|
throw new CommonError("密码错误")
|
|
}
|
|
|
|
// 生成token
|
|
const token = jwt.sign(
|
|
{ id: user.id, username: user.username },
|
|
JWT_SECRET,
|
|
{ expiresIn: "2h" }
|
|
)
|
|
|
|
// 返回token和用户信息
|
|
const { password: pwd, ...userInfo } = user
|
|
return { token, user: userInfo }
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`登录失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 根据用户名查找用户
|
|
async getUserByUsername(username) {
|
|
try {
|
|
if (!username) {
|
|
throw new CommonError("用户名不能为空")
|
|
}
|
|
|
|
const user = await UserModel.findByUsername(username)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
// 返回脱敏信息
|
|
const { password, ...userInfo } = user
|
|
return userInfo
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`获取用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 根据邮箱查找用户
|
|
async getUserByEmail(email) {
|
|
try {
|
|
if (!email) {
|
|
throw new CommonError("邮箱不能为空")
|
|
}
|
|
|
|
const user = await UserModel.findByEmail(email)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
// 返回脱敏信息
|
|
const { password, ...userInfo } = user
|
|
return userInfo
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`获取用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 修改密码
|
|
async changePassword(userId, oldPassword, newPassword) {
|
|
try {
|
|
if (!userId || !oldPassword || !newPassword) {
|
|
throw new CommonError("用户ID、旧密码和新密码不能为空")
|
|
}
|
|
|
|
const user = await UserModel.findById(userId)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
// 验证旧密码
|
|
const isOldPasswordCorrect = await comparePassword(oldPassword, user.password)
|
|
if (!isOldPasswordCorrect) {
|
|
throw new CommonError("旧密码错误")
|
|
}
|
|
|
|
// 加密新密码
|
|
const hashedNewPassword = await hashPassword(newPassword)
|
|
|
|
// 更新密码
|
|
await UserModel.update(userId, { password: hashedNewPassword })
|
|
|
|
return { message: "密码修改成功" }
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`修改密码失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 重置密码
|
|
async resetPassword(email, newPassword) {
|
|
try {
|
|
if (!email || !newPassword) {
|
|
throw new CommonError("邮箱和新密码不能为空")
|
|
}
|
|
|
|
const user = await UserModel.findByEmail(email)
|
|
if (!user) {
|
|
throw new CommonError("用户不存在")
|
|
}
|
|
|
|
// 加密新密码
|
|
const hashedPassword = await hashPassword(newPassword)
|
|
|
|
// 更新密码
|
|
await UserModel.update(user.id, { password: hashedPassword })
|
|
|
|
return { message: "密码重置成功" }
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`重置密码失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 获取用户统计信息
|
|
async getUserStats() {
|
|
try {
|
|
const users = await UserModel.findAll()
|
|
|
|
const stats = {
|
|
total: users.length,
|
|
active: users.filter(user => user.status === 'active').length,
|
|
inactive: users.filter(user => user.status === 'inactive').length,
|
|
byRole: {},
|
|
byDate: {}
|
|
}
|
|
|
|
// 按角色分组统计
|
|
users.forEach(user => {
|
|
const role = user.role || 'user'
|
|
stats.byRole[role] = (stats.byRole[role] || 0) + 1
|
|
})
|
|
|
|
// 按创建时间分组统计
|
|
users.forEach(user => {
|
|
const date = new Date(user.created_at).toISOString().split('T')[0]
|
|
stats.byDate[date] = (stats.byDate[date] || 0) + 1
|
|
})
|
|
|
|
return stats
|
|
} catch (error) {
|
|
throw new CommonError(`获取用户统计失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 搜索用户
|
|
async searchUsers(keyword) {
|
|
try {
|
|
if (!keyword || keyword.trim() === '') {
|
|
return await this.getAllUsers()
|
|
}
|
|
|
|
const users = await UserModel.findAll()
|
|
const searchTerm = keyword.toLowerCase().trim()
|
|
|
|
const filteredUsers = users.filter(user => {
|
|
return (
|
|
user.username?.toLowerCase().includes(searchTerm) ||
|
|
user.email?.toLowerCase().includes(searchTerm) ||
|
|
user.name?.toLowerCase().includes(searchTerm)
|
|
)
|
|
})
|
|
|
|
// 返回脱敏信息
|
|
return filteredUsers.map(user => {
|
|
const { password, ...userInfo } = user
|
|
return userInfo
|
|
})
|
|
} catch (error) {
|
|
throw new CommonError(`搜索用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 批量删除用户
|
|
async deleteUsers(userIds) {
|
|
try {
|
|
if (!Array.isArray(userIds) || userIds.length === 0) {
|
|
throw new CommonError("用户ID列表不能为空")
|
|
}
|
|
|
|
const results = []
|
|
const errors = []
|
|
|
|
for (const id of userIds) {
|
|
try {
|
|
await this.deleteUser(id)
|
|
results.push(id)
|
|
} catch (error) {
|
|
errors.push({
|
|
id,
|
|
error: error.message
|
|
})
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: results,
|
|
errors,
|
|
total: userIds.length,
|
|
successCount: results.length,
|
|
errorCount: errors.length
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`批量删除用户失败: ${error.message}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default UserService
|
|
|