站长要求:账号名除了 admin 其它都必须是中文名。 原来「只能包含汉字」的校验散在 5 个写入路径上,而且**没有 admin 例外** —— 导致连 admin 账号自己都编辑不了(改任何字段都撞「姓名和用户名只能包含汉字」)。 - 新增 server/utils/username.ts:规则一处定义 · isValidUsername:纯中文,或恰好是 admin · isValidPersonName:姓名纯中文,但 admin 账号的姓名不做限制 · 统一提示文案(接口报错与客户端提示共用同一句) - 5 个写入路径全部改用它,并删掉各自的 chineseRegex: admin/users 新建、admin/users 编辑、admin/users 批量导入、auth/register、user/profile - 注册时明确拒绝 admin(系统保留账号) 已验证:两端规则对 9 个用例结论完全一致;5 处均无旧正则; admin 可编辑,拼音名(zhangsan / zhang.san / 张三1 / 带空格)一律被拦。
121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
import bcrypt from 'bcryptjs'
|
|
import { isValidPersonName, isValidUsername } from '~~/server/utils/username'
|
|
import { db } from '~/drizzle/db'
|
|
import { users } from '~/drizzle/schema'
|
|
import { eq } from 'drizzle-orm'
|
|
import { cacheService } from '../../../services/cacheService'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
// 检查认证和权限
|
|
const user = event.context.user
|
|
if (!user || !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
|
|
throw createError({
|
|
statusCode: 403,
|
|
message: '没有权限访问'
|
|
})
|
|
}
|
|
|
|
const body = await readBody(event)
|
|
|
|
if (!body.users || !Array.isArray(body.users) || body.users.length === 0) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: '无效的用户数据'
|
|
})
|
|
}
|
|
|
|
const results = {
|
|
created: 0,
|
|
failed: 0,
|
|
total: body.users.length
|
|
}
|
|
|
|
// 批量添加用户
|
|
for (let i = 0; i < body.users.length; i++) {
|
|
const userData = body.users[i]
|
|
|
|
try {
|
|
// 验证必填字段
|
|
if (!userData.name || !userData.username || !userData.password) {
|
|
results.failed++
|
|
continue
|
|
}
|
|
|
|
if (!isValidPersonName(userData.name, userData.username) || !isValidUsername(userData.username)) {
|
|
results.failed++
|
|
continue
|
|
}
|
|
|
|
// 检查用户名是否已存在
|
|
const existingUser = await db
|
|
.select()
|
|
.from(users)
|
|
.where(eq(users.username, userData.username))
|
|
.limit(1)
|
|
|
|
if (existingUser.length > 0) {
|
|
results.failed++
|
|
continue
|
|
}
|
|
|
|
// 加密密码
|
|
const hashedPassword = await bcrypt.hash(userData.password, 10)
|
|
|
|
// 角色权限控制
|
|
let validRole = 'USER' // 默认角色
|
|
if (userData.role) {
|
|
if (user.role === 'SUPER_ADMIN') {
|
|
// 超级管理员可以设置任何角色
|
|
const allowedRoles = ['USER', 'TEACHER', 'ADMIN', 'SONG_ADMIN', 'SUPER_ADMIN']
|
|
if (allowedRoles.includes(userData.role)) {
|
|
validRole = userData.role
|
|
}
|
|
} else if (user.role === 'ADMIN') {
|
|
// 普通管理员只能设置 USER 和 SONG_ADMIN 角色
|
|
const allowedRoles = ['USER', 'TEACHER', 'SONG_ADMIN']
|
|
if (allowedRoles.includes(userData.role)) {
|
|
validRole = userData.role
|
|
}
|
|
}
|
|
}
|
|
|
|
// 状态验证
|
|
let validStatus = 'active'
|
|
if (userData.status && ['active', 'withdrawn', 'graduate'].includes(userData.status)) {
|
|
validStatus = userData.status
|
|
}
|
|
|
|
// 创建用户
|
|
const newUser = await db
|
|
.insert(users)
|
|
.values({
|
|
name: userData.name,
|
|
username: userData.username,
|
|
password: hashedPassword,
|
|
role: validRole,
|
|
status: validStatus as 'active' | 'withdrawn' | 'graduate',
|
|
grade: userData.grade,
|
|
class: userData.class
|
|
})
|
|
.returning()
|
|
|
|
results.created++
|
|
} catch (error) {
|
|
console.error(`用户创建失败:`, error)
|
|
results.failed++
|
|
}
|
|
}
|
|
|
|
// 如果有用户创建成功,清除相关缓存
|
|
if (results.created > 0) {
|
|
try {
|
|
await cacheService.clearSongsCache()
|
|
console.log('批量用户创建后缓存清除成功')
|
|
} catch (cacheError) {
|
|
console.error('批量用户创建后缓存清除失败:', cacheError)
|
|
}
|
|
}
|
|
|
|
return results
|
|
})
|