271 lines
8.2 KiB
TypeScript
271 lines
8.2 KiB
TypeScript
/**
|
||
* useUserOperations Hook
|
||
*
|
||
* 用户操作Hook
|
||
* 统一管理用户相关的操作(更新、删除、切换状态等)
|
||
* 包括:更新用户、删除用户、切换下载权限、切换激活状态、设置角色、转移最高权限
|
||
*
|
||
* @created 2025-01-XX(从 page.tsx 提取)
|
||
* @hook
|
||
*/
|
||
|
||
'use client'
|
||
|
||
import { useCallback } from 'react'
|
||
import type { User, Department, ApiSuccessResponse } from '../_components/types'
|
||
import { parseErrorResponse } from '../_utils/api'
|
||
import type { ToastFn } from '../_components/types'
|
||
|
||
interface UseUserOperationsOptions {
|
||
users: User[]
|
||
setUsers: React.Dispatch<React.SetStateAction<User[]>>
|
||
showToast: ToastFn
|
||
fetchData: () => Promise<void>
|
||
findDepartmentManagers: (departmentName: string | null | undefined) => User[] | null
|
||
}
|
||
|
||
export function useUserOperations({
|
||
users,
|
||
setUsers,
|
||
showToast,
|
||
fetchData,
|
||
findDepartmentManagers,
|
||
}: UseUserOperationsOptions) {
|
||
const updateUser = useCallback(
|
||
async (user: User, payload: { username: string; department: string }) => {
|
||
const { username, department } = payload
|
||
const usernameChanged = username !== user.username
|
||
const departmentChanged = department !== (user.department || '')
|
||
|
||
if (!usernameChanged && !departmentChanged) {
|
||
showToast('未做任何修改', 'error')
|
||
return false
|
||
}
|
||
|
||
try {
|
||
let materialsUpdated = false
|
||
let parentIdToSet: string | null = null
|
||
|
||
if (departmentChanged && department) {
|
||
const managers = findDepartmentManagers(department) || []
|
||
if (managers.length > 0) {
|
||
const firstManager = managers.find((manager) => manager.id !== user.id) || managers[0]
|
||
parentIdToSet = firstManager.id
|
||
}
|
||
}
|
||
|
||
if (usernameChanged || departmentChanged) {
|
||
const response = await fetch('/api/admin/users/update', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
userId: user.id,
|
||
username,
|
||
department,
|
||
}),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
const errorData = await parseErrorResponse(response)
|
||
showToast(errorData.error || '更新失败', 'error')
|
||
return false
|
||
}
|
||
const successData = (await response.json()) as ApiSuccessResponse<{ materialsUpdated?: boolean }>
|
||
materialsUpdated = Boolean(successData.materialsUpdated)
|
||
|
||
if (departmentChanged && parentIdToSet) {
|
||
const hierarchyResponse = await fetch('/api/admin/users/update-hierarchy', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
userId: user.id,
|
||
parentId: parentIdToSet,
|
||
}),
|
||
})
|
||
|
||
if (!hierarchyResponse.ok) {
|
||
const errorData = await parseErrorResponse(hierarchyResponse)
|
||
showToast(errorData.error || '调整上下级失败', 'error')
|
||
return false
|
||
}
|
||
}
|
||
}
|
||
|
||
await fetchData()
|
||
showToast(materialsUpdated ? '用户信息已更新,素材信息已同步' : '用户信息已更新')
|
||
return true
|
||
} catch (error) {
|
||
console.error('Update user error:', error)
|
||
showToast('更新失败,请稍后重试', 'error')
|
||
return false
|
||
}
|
||
},
|
||
[showToast, fetchData, findDepartmentManagers]
|
||
)
|
||
|
||
const deleteUser = useCallback(
|
||
async (id: string) => {
|
||
try {
|
||
const response = await fetch(`/api/admin/users?id=${id}`, {
|
||
method: 'DELETE',
|
||
})
|
||
|
||
if (response.ok) {
|
||
setUsers((prevUsers) => prevUsers.filter((u) => u.id !== id))
|
||
showToast('删除成功')
|
||
return true
|
||
}
|
||
const errorData = await parseErrorResponse(response)
|
||
showToast(errorData.error || '删除失败', 'error')
|
||
return false
|
||
} catch (error) {
|
||
console.error('Error deleting user:', error)
|
||
showToast('删除失败', 'error')
|
||
return false
|
||
}
|
||
},
|
||
[setUsers, showToast]
|
||
)
|
||
|
||
const toggleDownload = useCallback(
|
||
async (userId: string, currentCanDownload: boolean) => {
|
||
const action = currentCanDownload ? '关闭' : '开启'
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/users/toggle-download', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ userId }),
|
||
})
|
||
|
||
if (response.ok) {
|
||
// 直接更新本地状态,避免重新加载整个列表
|
||
setUsers((prevUsers) =>
|
||
prevUsers.map((user) =>
|
||
user.id === userId ? { ...user, canDownload: !currentCanDownload } : user
|
||
)
|
||
)
|
||
showToast(`下载权限已${action}`)
|
||
} else {
|
||
const errorData = await parseErrorResponse(response)
|
||
showToast(errorData.error || '操作失败', 'error')
|
||
}
|
||
} catch (error) {
|
||
console.error('Error toggling download permission:', error)
|
||
showToast('操作失败', 'error')
|
||
}
|
||
},
|
||
[setUsers, showToast]
|
||
)
|
||
|
||
const toggleActive = useCallback(
|
||
async (userId: string, currentStatus: boolean) => {
|
||
const action = currentStatus ? '禁用' : '激活'
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/users/toggle-active', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
userId,
|
||
isActive: !currentStatus,
|
||
}),
|
||
})
|
||
|
||
if (response.ok) {
|
||
// 直接更新本地状态
|
||
setUsers((prevUsers) =>
|
||
prevUsers.map((user) => (user.id === userId ? { ...user, isActive: !currentStatus } : user))
|
||
)
|
||
showToast(`${action}成功`)
|
||
} else {
|
||
const errorData = await parseErrorResponse(response)
|
||
showToast(errorData.error || `${action}失败`, 'error')
|
||
}
|
||
} catch (error) {
|
||
console.error('Toggle active error:', error)
|
||
showToast(`${action}失败`, 'error')
|
||
}
|
||
},
|
||
[setUsers, showToast]
|
||
)
|
||
|
||
const setRole = useCallback(
|
||
async (userId: string, currentRole: string) => {
|
||
const newRole = currentRole === 'ADMIN' ? 'USER' : 'ADMIN'
|
||
const action = newRole === 'ADMIN' ? '设置为管理员' : '取消管理员权限'
|
||
|
||
try {
|
||
const response = await fetch('/api/admin/users/set-role', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
userId,
|
||
role: newRole,
|
||
}),
|
||
})
|
||
|
||
if (response.ok) {
|
||
// 刷新数据,用户树会同步更新最新的管理员信息
|
||
await fetchData()
|
||
showToast(`${action}成功`)
|
||
} else {
|
||
const errorData = await parseErrorResponse(response)
|
||
showToast(errorData.error || `${action}失败`, 'error')
|
||
}
|
||
} catch (error) {
|
||
console.error('Set role error:', error)
|
||
showToast(`${action}失败`, 'error')
|
||
}
|
||
},
|
||
[showToast, fetchData]
|
||
)
|
||
|
||
const transferSuperAdmin = useCallback(
|
||
async (targetEmail: string, password: string) => {
|
||
try {
|
||
const res = await fetch('/api/admin/transfer-superadmin', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ targetEmail, password }),
|
||
})
|
||
if (res.ok) {
|
||
showToast('最高权限已转移')
|
||
await fetchData()
|
||
return true
|
||
}
|
||
const errorData = await parseErrorResponse(res)
|
||
showToast(errorData.error || '操作失败', 'error')
|
||
return false
|
||
} catch (err) {
|
||
console.error('transfer super admin error:', err)
|
||
showToast('操作失败,请稍后重试', 'error')
|
||
return false
|
||
}
|
||
},
|
||
[showToast, fetchData]
|
||
)
|
||
|
||
return {
|
||
updateUser,
|
||
deleteUser,
|
||
toggleDownload,
|
||
toggleActive,
|
||
setRole,
|
||
transferSuperAdmin,
|
||
}
|
||
}
|
||
|
||
export type UseUserOperationsReturn = ReturnType<typeof useUserOperations>
|
||
|