flnxi-Wu/app/admin/_hooks/useUserOperations.ts
2025-11-22 17:56:24 +08:00

271 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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>