fix: 接入用户与权限后端接口
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import { ApiResponseError, unwrapApiResponse } from '@/utils/api-response'
|
||||
import {
|
||||
ApiResponseError,
|
||||
unwrapApiResponse,
|
||||
unwrapNullableApiResponse,
|
||||
} from '@/utils/api-response'
|
||||
import { request } from '@/utils/request'
|
||||
import type {
|
||||
AuthenticatedUser,
|
||||
@@ -8,6 +12,7 @@ import type {
|
||||
CurrentUserResponse,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
PasswordChangeRequest,
|
||||
} from '@/types/auth'
|
||||
import type { ApiResponse } from '@/types/common'
|
||||
|
||||
@@ -76,6 +81,11 @@ export async function logout(): Promise<void> {
|
||||
unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function changePassword(payload: PasswordChangeRequest): Promise<void> {
|
||||
const response = await request.post<ApiResponse<null>>('/v1/auth/password/change', payload)
|
||||
unwrapNullableApiResponse(response)
|
||||
}
|
||||
|
||||
export function getAuthErrorMessage(error: unknown) {
|
||||
if (error instanceof ApiResponseError) {
|
||||
return error.message
|
||||
|
||||
@@ -5,9 +5,12 @@ import type { ApiResponse } from '@/types/common'
|
||||
import type {
|
||||
BackendCollection,
|
||||
CreateRoleRequest,
|
||||
RolePermissionsResponseDto,
|
||||
RolePermissionsUpdateRequest,
|
||||
RoleRecord,
|
||||
RoleQuery,
|
||||
RoleResponseDto,
|
||||
RoleSummaryResponseDto,
|
||||
UpdateRoleRequest,
|
||||
} from './types'
|
||||
|
||||
@@ -15,18 +18,35 @@ const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const fallbackColors = ['#6b5bd2', '#0e6e8c', '#0f9d6c', '#d9821f', '#c65d5d']
|
||||
|
||||
function normalizeRole(dto: RoleResponseDto, index: number): RoleRecord {
|
||||
function normalizeRole(dto: RoleResponseDto, index: number, userCount?: number | null): RoleRecord {
|
||||
return {
|
||||
id: dto.id,
|
||||
code: dto.code,
|
||||
name: dto.name,
|
||||
description: dto.description ?? '—',
|
||||
color: fallbackColors[index % fallbackColors.length],
|
||||
userCount: dto.userCount ?? undefined,
|
||||
userCount: userCount ?? undefined,
|
||||
status: dto.status ?? 'ENABLED',
|
||||
createdAt: dto.createdAt,
|
||||
updatedAt: dto.updatedAt,
|
||||
version: dto.version ?? undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRoleSummary(dto: RoleSummaryResponseDto, index: number): RoleRecord {
|
||||
return normalizeRole(
|
||||
{
|
||||
id: dto.roleId,
|
||||
code: dto.code,
|
||||
name: dto.name,
|
||||
description: dto.description,
|
||||
status: dto.status,
|
||||
},
|
||||
index,
|
||||
dto.userCount,
|
||||
)
|
||||
}
|
||||
|
||||
function toBackendQuery(query: RoleQuery) {
|
||||
const params: Record<string, string | number> = {
|
||||
page: Math.max(query.page, 1),
|
||||
@@ -54,10 +74,59 @@ export function getRoles(query: RoleQuery = { page: 1, pageSize: 200 }): Promise
|
||||
const records = Array.isArray(data)
|
||||
? data
|
||||
: (data.records ?? data.items ?? data.content ?? [])
|
||||
return records.map(normalizeRole)
|
||||
return records.map((role, index) => normalizeRole(role, index))
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleSummaries(campusId?: string): Promise<RoleRecord[]> {
|
||||
if (useMockData) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
return request
|
||||
.get<ApiResponse<BackendCollection<RoleSummaryResponseDto>>>('/v1/roles/summary', {
|
||||
params: campusId ? { campusId } : undefined,
|
||||
})
|
||||
.then(unwrapApiResponse)
|
||||
.then((data) => {
|
||||
const records = Array.isArray(data)
|
||||
? data
|
||||
: (data.records ?? data.items ?? data.content ?? [])
|
||||
return records.map(normalizeRoleSummary)
|
||||
})
|
||||
}
|
||||
|
||||
export async function getRolePermissions(roleId: string): Promise<RolePermissionsResponseDto> {
|
||||
if (useMockData) {
|
||||
return {
|
||||
roleId,
|
||||
permissions: [],
|
||||
version: 0,
|
||||
updatedAt: '',
|
||||
}
|
||||
}
|
||||
|
||||
const response = await request.get<ApiResponse<RolePermissionsResponseDto>>(
|
||||
`/v1/roles/${encodeURIComponent(roleId)}/permissions`,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function updateRolePermissions(
|
||||
roleId: string,
|
||||
payload: RolePermissionsUpdateRequest,
|
||||
): Promise<RolePermissionsResponseDto> {
|
||||
if (useMockData) {
|
||||
throw new Error('Mock 模式不执行角色权限写操作')
|
||||
}
|
||||
|
||||
const response = await request.put<ApiResponse<RolePermissionsResponseDto>>(
|
||||
`/v1/roles/${encodeURIComponent(roleId)}/permissions`,
|
||||
payload,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export function createRole(payload: CreateRoleRequest): Promise<RoleRecord> {
|
||||
if (useMockData) {
|
||||
return Promise.reject(new Error('Mock 模式不执行角色写操作'))
|
||||
|
||||
@@ -124,6 +124,9 @@ export interface RoleRecord {
|
||||
color?: string
|
||||
userCount?: number
|
||||
status: RoleStatus
|
||||
createdAt?: string | null
|
||||
updatedAt?: string | null
|
||||
version?: number
|
||||
}
|
||||
|
||||
export interface RoleQuery extends PageQuery {
|
||||
@@ -138,6 +141,17 @@ export interface RoleResponseDto {
|
||||
name: string
|
||||
description?: string | null
|
||||
status?: RoleStatus | null
|
||||
createdAt?: string | null
|
||||
updatedAt?: string | null
|
||||
version?: number | null
|
||||
}
|
||||
|
||||
export interface RoleSummaryResponseDto {
|
||||
roleId: string
|
||||
code: string
|
||||
name: string
|
||||
description?: string | null
|
||||
status?: RoleStatus | null
|
||||
userCount?: number | null
|
||||
}
|
||||
|
||||
@@ -391,7 +405,7 @@ export interface ReportExportJobResponseDto {
|
||||
downloadPath?: string | null
|
||||
}
|
||||
|
||||
export type UserStatus = 'enabled' | 'disabled'
|
||||
export type UserStatus = 'enabled' | 'disabled' | 'locked'
|
||||
|
||||
export interface UserQuery extends PageQuery {
|
||||
keyword?: string
|
||||
@@ -416,7 +430,10 @@ export interface UserRecord {
|
||||
departmentId?: string | null
|
||||
departmentName?: string
|
||||
roleNames?: string[]
|
||||
roleIds?: string[]
|
||||
dataScope?: string
|
||||
version?: number
|
||||
passwordChangeRequired?: boolean
|
||||
}
|
||||
|
||||
export type UserListResponse = PageResult<UserRecord>
|
||||
@@ -429,12 +446,24 @@ export interface UserResponseDto {
|
||||
phone?: string | null
|
||||
email?: string | null
|
||||
campusId?: string | null
|
||||
campusName?: string | null
|
||||
departmentId?: string | null
|
||||
departmentName?: string | null
|
||||
roles?: UserRoleSummaryDto[] | null
|
||||
dataScope?: string | null
|
||||
status?: string | null
|
||||
status?: BackendUserStatus | null
|
||||
lastLoginAt?: string | null
|
||||
createdAt?: string | null
|
||||
updatedAt?: string | null
|
||||
version?: number | null
|
||||
passwordChangeRequired?: boolean
|
||||
}
|
||||
|
||||
export interface UserRoleSummaryDto {
|
||||
id: string
|
||||
code: string
|
||||
name: string
|
||||
status?: RoleStatus | null
|
||||
}
|
||||
|
||||
export interface CreateUserRequest {
|
||||
@@ -448,10 +477,11 @@ export interface CreateUserRequest {
|
||||
departmentId?: string | null
|
||||
status?: BackendUserStatus
|
||||
dataScope?: BackendDataScope
|
||||
roleIds?: string[]
|
||||
}
|
||||
|
||||
export type BackendUserStatus = 'ENABLED' | 'DISABLED' | string
|
||||
export type BackendDataScope = 'ALL' | 'CAMPUS' | 'DEPARTMENT' | 'READ_ONLY_ALL' | string
|
||||
export type BackendUserStatus = 'ENABLED' | 'DISABLED' | 'LOCKED'
|
||||
export type BackendDataScope = 'ALL' | 'CAMPUS' | 'DEPARTMENT' | 'READ_ONLY_ALL'
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
displayName: string
|
||||
@@ -462,6 +492,90 @@ export interface UpdateUserRequest {
|
||||
departmentId?: string | null
|
||||
status: BackendUserStatus
|
||||
dataScope: BackendDataScope
|
||||
clearPhone?: boolean
|
||||
clearEmail?: boolean
|
||||
expectedVersion?: number
|
||||
}
|
||||
|
||||
export interface UserManagementUpdateRequest {
|
||||
displayName: string
|
||||
employeeNo?: string
|
||||
phone?: string
|
||||
email?: string
|
||||
clearPhone?: boolean
|
||||
clearEmail?: boolean
|
||||
campusId: string
|
||||
departmentId?: string | null
|
||||
status: BackendUserStatus
|
||||
dataScope: BackendDataScope
|
||||
roleIds: string[]
|
||||
expectedVersion?: number
|
||||
}
|
||||
|
||||
export interface UserRolesResponseDto {
|
||||
userId: string
|
||||
roles: UserRoleSummaryDto[]
|
||||
version: number
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface UserRolesUpdateRequest {
|
||||
roleIds: string[]
|
||||
expectedVersion?: number
|
||||
}
|
||||
|
||||
export interface RolePermissionsResponseDto {
|
||||
roleId: string
|
||||
permissions: ApiPermissionResponseDto[]
|
||||
version: number
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface RolePermissionsUpdateRequest {
|
||||
permissionIds: string[]
|
||||
expectedVersion?: number
|
||||
}
|
||||
|
||||
export interface PasswordResetRequest {
|
||||
newPassword: string
|
||||
forceChangeOnNextLogin?: boolean
|
||||
}
|
||||
|
||||
export type UserImportRowStatus = 'VALID' | 'INVALID'
|
||||
|
||||
export interface UserImportPreviewRowDto {
|
||||
rowNo: number
|
||||
status: UserImportRowStatus
|
||||
errors: string[]
|
||||
username?: string | null
|
||||
displayName?: string | null
|
||||
}
|
||||
|
||||
export interface UserImportPreviewResponseDto {
|
||||
importId: string
|
||||
fileName: string
|
||||
totalRows: number
|
||||
validRows: number
|
||||
invalidRows: number
|
||||
rows: UserImportPreviewRowDto[]
|
||||
expiresAt: string
|
||||
}
|
||||
|
||||
export interface UserImportCommitRequest {
|
||||
importId: string
|
||||
}
|
||||
|
||||
export type UserImportJobStatus = 'PREVIEWED' | 'COMMITTED' | 'EXPIRED' | 'FAILED' | string
|
||||
|
||||
export interface UserImportJobResponseDto {
|
||||
jobId: string
|
||||
status: UserImportJobStatus
|
||||
totalRows?: number | null
|
||||
validRows?: number | null
|
||||
invalidRows?: number | null
|
||||
createdAt?: string | null
|
||||
expiresAt?: string | null
|
||||
committedAt?: string | null
|
||||
}
|
||||
|
||||
export type PermissionSubjectType = 'user' | 'role'
|
||||
|
||||
@@ -8,11 +8,18 @@ import type {
|
||||
BackendCollection,
|
||||
BackendPage,
|
||||
CreateUserRequest,
|
||||
PasswordResetRequest,
|
||||
UpdateUserRequest,
|
||||
UserImportCommitRequest,
|
||||
UserImportJobResponseDto,
|
||||
UserImportPreviewResponseDto,
|
||||
UserManagementUpdateRequest,
|
||||
UserListResponse,
|
||||
UserQuery,
|
||||
UserRecord,
|
||||
UserResponseDto,
|
||||
UserRolesResponseDto,
|
||||
UserRolesUpdateRequest,
|
||||
} from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
@@ -70,24 +77,37 @@ function formatDateTime(value: string | null | undefined) {
|
||||
}
|
||||
|
||||
function normalizeStatus(value: string | null | undefined): UserRecord['status'] {
|
||||
if (value === 'LOCKED') {
|
||||
return 'locked'
|
||||
}
|
||||
|
||||
return value === 'DISABLED' || value === 'INACTIVE' ? 'disabled' : 'enabled'
|
||||
}
|
||||
|
||||
function mapUser(dto: UserResponseDto): UserRecord {
|
||||
const roles = dto.roles ?? []
|
||||
const roleNames = roles.map((role) => role.name).filter(Boolean)
|
||||
|
||||
return {
|
||||
id: dto.id,
|
||||
name: dto.displayName,
|
||||
account: dto.username,
|
||||
department: dto.departmentId ? '未知科室' : '未指定科室',
|
||||
role: '未返回角色',
|
||||
department: dto.departmentName ?? (dto.departmentId ? '未知科室' : '未指定科室'),
|
||||
role: roleNames.length ? roleNames.join('、') : '未分配角色',
|
||||
status: normalizeStatus(dto.status),
|
||||
lastLoginAt: formatDateTime(dto.lastLoginAt),
|
||||
employeeNo: dto.employeeNo,
|
||||
phone: dto.phone,
|
||||
email: dto.email,
|
||||
campusId: dto.campusId,
|
||||
campusName: dto.campusName ?? undefined,
|
||||
departmentId: dto.departmentId,
|
||||
departmentName: dto.departmentName ?? undefined,
|
||||
roleNames,
|
||||
roleIds: roles.map((role) => role.id),
|
||||
dataScope: dto.dataScope ?? '—',
|
||||
version: dto.version ?? undefined,
|
||||
passwordChangeRequired: dto.passwordChangeRequired,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +223,113 @@ export function updateUser(id: string, payload: UpdateUserRequest): Promise<User
|
||||
.then(mapUser)
|
||||
}
|
||||
|
||||
export function updateUserManagement(
|
||||
id: string,
|
||||
payload: UserManagementUpdateRequest,
|
||||
): Promise<UserRecord> {
|
||||
if (useMockData) {
|
||||
return Promise.reject(new Error('Mock 模式不执行用户写操作'))
|
||||
}
|
||||
|
||||
return request
|
||||
.put<ApiResponse<UserResponseDto>>(`/v1/users/${encodeURIComponent(id)}/management`, payload)
|
||||
.then(unwrapApiResponse)
|
||||
.then(mapUser)
|
||||
}
|
||||
|
||||
export async function getUserRoles(userId: string): Promise<UserRolesResponseDto> {
|
||||
if (useMockData) {
|
||||
return {
|
||||
userId,
|
||||
roles: [],
|
||||
version: 0,
|
||||
updatedAt: '',
|
||||
}
|
||||
}
|
||||
|
||||
const response = await request.get<ApiResponse<UserRolesResponseDto>>(
|
||||
`/v1/users/${encodeURIComponent(userId)}/roles`,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function updateUserRoles(
|
||||
userId: string,
|
||||
payload: UserRolesUpdateRequest,
|
||||
): Promise<UserRolesResponseDto> {
|
||||
if (useMockData) {
|
||||
return Promise.reject(new Error('Mock 模式不执行用户角色写操作'))
|
||||
}
|
||||
|
||||
const response = await request.put<ApiResponse<UserRolesResponseDto>>(
|
||||
`/v1/users/${encodeURIComponent(userId)}/roles`,
|
||||
payload,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function resetUserPassword(
|
||||
userId: string,
|
||||
payload: PasswordResetRequest,
|
||||
): Promise<void> {
|
||||
if (useMockData) {
|
||||
throw new Error('Mock 模式不执行密码重置')
|
||||
}
|
||||
|
||||
const response = await request.post<ApiResponse<null>>(
|
||||
`/v1/users/${encodeURIComponent(userId)}/password/reset`,
|
||||
payload,
|
||||
)
|
||||
unwrapNullableApiResponse(response)
|
||||
}
|
||||
|
||||
export async function previewUserImport(
|
||||
file: File,
|
||||
campusId?: string,
|
||||
): Promise<UserImportPreviewResponseDto> {
|
||||
if (useMockData) {
|
||||
throw new Error('Mock 模式不执行用户导入')
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
if (campusId) {
|
||||
formData.append('campusId', campusId)
|
||||
}
|
||||
|
||||
const response = await request.post<ApiResponse<UserImportPreviewResponseDto>>(
|
||||
'/v1/users/import/preview',
|
||||
formData,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function commitUserImport(
|
||||
payload: UserImportCommitRequest,
|
||||
): Promise<UserImportJobResponseDto> {
|
||||
if (useMockData) {
|
||||
throw new Error('Mock 模式不执行用户导入')
|
||||
}
|
||||
|
||||
const response = await request.post<ApiResponse<UserImportJobResponseDto>>(
|
||||
'/v1/users/import/commit',
|
||||
payload,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function getUserImportJob(jobId: string): Promise<UserImportJobResponseDto> {
|
||||
if (useMockData) {
|
||||
throw new Error('Mock 模式不执行用户导入')
|
||||
}
|
||||
|
||||
const response = await request.get<ApiResponse<UserImportJobResponseDto>>(
|
||||
`/v1/users/import/jobs/${encodeURIComponent(jobId)}`,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export function deleteUser(id: string): Promise<void> {
|
||||
if (useMockData) {
|
||||
return Promise.reject(new Error('Mock 模式不执行用户写操作'))
|
||||
|
||||
Reference in New Issue
Block a user