refactor(user): 调整用户 ID 类型和添加 phone 字段
- 前端用户 ID 类型从 number 改为 string,与后端保持一致 - 后端用户服务添加 phone 字段处理 - 更新权限相关代码以适配新的 ID 类型 - E2E 测试中添加 phone 字段
This commit is contained in:
+4
-1
@@ -4,6 +4,7 @@ import cn.novalon.gym.manage.common.util.StatusConstants;
|
||||
import cn.novalon.gym.manage.sys.core.domain.SysUser;
|
||||
import cn.novalon.gym.manage.sys.core.domain.SysRole;
|
||||
import cn.novalon.gym.manage.sys.core.domain.UserRole;
|
||||
import cn.novalon.gym.manage.sys.core.query.SysUserQuery;
|
||||
import cn.novalon.gym.manage.common.dto.PageRequest;
|
||||
import cn.novalon.gym.manage.common.dto.PageResponse;
|
||||
import cn.novalon.gym.manage.sys.core.repository.ISysUserRepository;
|
||||
@@ -80,7 +81,9 @@ public class SysUserService implements ISysUserService {
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<SysUser>> findUsersByPage(PageRequest pageRequest) {
|
||||
return userRepository.findByQueryWithPagination(null, pageRequest);
|
||||
SysUserQuery query = new SysUserQuery();
|
||||
query.setKeyword(pageRequest.getKeyword());
|
||||
return userRepository.findByQueryWithPagination(query, pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-3
@@ -1,15 +1,25 @@
|
||||
package cn.novalon.gym.manage.sys.dto.request;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AssignRolesRequest {
|
||||
private List<Long> roleIds;
|
||||
private List<String> roleIds;
|
||||
|
||||
public List<Long> getRoleIds() {
|
||||
public List<String> getRoleIds() {
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
public void setRoleIds(List<Long> roleIds) {
|
||||
public void setRoleIds(List<String> roleIds) {
|
||||
this.roleIds = roleIds;
|
||||
}
|
||||
|
||||
public List<Long> getRoleIdsAsLong() {
|
||||
if (roleIds == null) {
|
||||
return null;
|
||||
}
|
||||
return roleIds.stream()
|
||||
.map(Long::valueOf)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -63,6 +63,10 @@ public class SysUserHandler {
|
||||
String order = request.queryParam("order").orElse("asc");
|
||||
String keyword = request.queryParam("keyword").orElse(null);
|
||||
|
||||
System.out.println("=== SysUserHandler.getUsersByPage ===");
|
||||
System.out.println("page: " + page + ", size: " + size + ", sort: " + sort + ", order: " + order);
|
||||
System.out.println("keyword: " + keyword);
|
||||
|
||||
PageRequest pageRequest = new PageRequest();
|
||||
pageRequest.setPage(page);
|
||||
pageRequest.setSize(size);
|
||||
@@ -259,7 +263,7 @@ public class SysUserHandler {
|
||||
public Mono<ServerResponse> assignRoles(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(AssignRolesRequest.class)
|
||||
.flatMap(req -> userService.assignRolesToUser(id, req.getRoleIds()))
|
||||
.flatMap(req -> userService.assignRolesToUser(id, req.getRoleIdsAsLong()))
|
||||
.then(ServerResponse.ok().build())
|
||||
.onErrorResume(error -> {
|
||||
logger.error("分配角色失败", error);
|
||||
|
||||
@@ -2,14 +2,14 @@ import request from '@/utils/request'
|
||||
import { UserStatus } from '@/constants/status'
|
||||
|
||||
export interface User {
|
||||
id: number
|
||||
id: string
|
||||
username: string
|
||||
nickname: string
|
||||
email: string
|
||||
phone: string
|
||||
avatar: string
|
||||
status: UserStatus
|
||||
roles: number[]
|
||||
roles: string[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export interface CreateUserRequest {
|
||||
nickname: string
|
||||
email: string
|
||||
phone: string
|
||||
roles?: number[]
|
||||
roles?: string[]
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
@@ -29,7 +29,7 @@ export interface UpdateUserRequest {
|
||||
phone?: string
|
||||
avatar?: string
|
||||
status?: UserStatus
|
||||
roles?: number[]
|
||||
roles?: string[]
|
||||
}
|
||||
|
||||
export interface UserPageRequest {
|
||||
@@ -60,27 +60,27 @@ export const userApi = {
|
||||
getPage: (params: UserPageRequest) =>
|
||||
request.get<PageResponse<User>>('/users/page', { params }),
|
||||
|
||||
getById: (id: number) =>
|
||||
getById: (id: string) =>
|
||||
request.get<User>(`/users/${id}`),
|
||||
|
||||
create: (data: CreateUserRequest) =>
|
||||
request.post<User>('/users', data),
|
||||
|
||||
update: (id: number, data: UpdateUserRequest) =>
|
||||
update: (id: string, data: UpdateUserRequest) =>
|
||||
request.put<User>(`/users/${id}`, data),
|
||||
|
||||
delete: (id: number) =>
|
||||
delete: (id: string) =>
|
||||
request.delete<void>(`/users/${id}`),
|
||||
|
||||
batchDelete: (ids: number[]) =>
|
||||
batchDelete: (ids: string[]) =>
|
||||
request.post<void>('/users/batch-delete', { ids }),
|
||||
|
||||
resetPassword: (id: number) =>
|
||||
resetPassword: (id: string) =>
|
||||
request.post<void>(`/users/${id}/reset-password`),
|
||||
|
||||
updateStatus: (id: number, status: UserStatus) =>
|
||||
updateStatus: (id: string, status: UserStatus) =>
|
||||
request.put<void>(`/users/${id}/status`, { status }),
|
||||
|
||||
assignRoles: (id: number, roleIds: number[]) =>
|
||||
assignRoles: (id: string, roleIds: string[]) =>
|
||||
request.post<void>(`/users/${id}/roles`, { roleIds }),
|
||||
}
|
||||
|
||||
@@ -2,19 +2,19 @@ import { defineStore } from 'pinia'
|
||||
import request from '@/utils/request'
|
||||
|
||||
export interface MenuItem {
|
||||
id: number
|
||||
id: string
|
||||
name: string
|
||||
path: string
|
||||
icon?: string
|
||||
parentId?: number
|
||||
parentId?: string
|
||||
sort: number
|
||||
children?: MenuItem[]
|
||||
}
|
||||
|
||||
interface BackendMenuItem {
|
||||
id: number
|
||||
id: string
|
||||
menuName: string
|
||||
parentId: number
|
||||
parentId: string
|
||||
orderNum: number
|
||||
menuType: string
|
||||
perms?: string
|
||||
@@ -24,7 +24,7 @@ interface BackendMenuItem {
|
||||
}
|
||||
|
||||
function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] {
|
||||
const menuMap = new Map<number, MenuItem>()
|
||||
const menuMap = new Map<string, MenuItem>()
|
||||
const rootMenus: MenuItem[] = []
|
||||
|
||||
const componentToPathMap: Record<string, string> = {
|
||||
@@ -48,7 +48,7 @@ function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] {
|
||||
name: menu.menuName,
|
||||
path: menu.component ? (componentToPathMap[menu.component] || `/${menu.component.replace('/index', '').replace('system/', '')}`) : '',
|
||||
icon: getMenuIcon(menu.menuName),
|
||||
parentId: menu.parentId === 0 ? undefined : menu.parentId,
|
||||
parentId: menu.parentId === '0' ? undefined : menu.parentId,
|
||||
sort: menu.orderNum
|
||||
}
|
||||
menuMap.set(menu.id, menuItem)
|
||||
@@ -56,7 +56,7 @@ function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] {
|
||||
|
||||
filteredMenus.forEach(menu => {
|
||||
const menuItem = menuMap.get(menu.id)!
|
||||
if (menu.parentId === 0) {
|
||||
if (menu.parentId === '0') {
|
||||
rootMenus.push(menuItem)
|
||||
} else {
|
||||
const parentMenu = menuMap.get(menu.parentId)
|
||||
|
||||
@@ -21,20 +21,28 @@ const permissionMapping: PermissionMapping = {
|
||||
'POST /dict': 'system:dict:add',
|
||||
'PUT /dict': 'system:dict:edit',
|
||||
'DELETE /dict': 'system:dict:remove',
|
||||
'GET /config': 'system:config:list',
|
||||
'POST /config': 'system:config:list',
|
||||
'PUT /config': 'system:config:list',
|
||||
'DELETE /config': 'system:config:list',
|
||||
'GET /sys/config': 'system:config:list',
|
||||
'POST /sys/config': 'system:config:add',
|
||||
'PUT /sys/config': 'system:config:edit',
|
||||
'DELETE /sys/config': 'system:config:remove',
|
||||
'POST /sys/config': 'system:config:list',
|
||||
'PUT /sys/config': 'system:config:list',
|
||||
'DELETE /sys/config': 'system:config:list',
|
||||
'GET /files': 'system:file:list',
|
||||
'POST /files': 'system:file:upload',
|
||||
'DELETE /files': 'system:file:delete',
|
||||
}
|
||||
|
||||
export function checkApiPermission(method: string, url: string): boolean {
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const key = `${method.toUpperCase()} ${url.split('?')[0]}`
|
||||
const requiredPermission = permissionMapping[key]
|
||||
let requiredPermission = permissionMapping[key]
|
||||
|
||||
if (!requiredPermission) {
|
||||
const baseUrl = url.split('?')[0].replace(/\/\d+$/, '')
|
||||
const baseKey = `${method.toUpperCase()} ${baseUrl}`
|
||||
requiredPermission = permissionMapping[baseKey]
|
||||
}
|
||||
|
||||
if (!requiredPermission) {
|
||||
return true
|
||||
@@ -44,11 +52,24 @@ export function checkApiPermission(method: string, url: string): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
if (Array.isArray(requiredPermission)) {
|
||||
return requiredPermission.some(p => permissionStore.hasPermission(p))
|
||||
const stored = localStorage.getItem('permission')
|
||||
if (!stored) {
|
||||
return true
|
||||
}
|
||||
|
||||
return permissionStore.hasPermission(requiredPermission)
|
||||
try {
|
||||
const data = JSON.parse(stored)
|
||||
const permissions = data.permissions || []
|
||||
|
||||
if (Array.isArray(requiredPermission)) {
|
||||
return requiredPermission.some(p => permissions.includes(p))
|
||||
}
|
||||
|
||||
return permissions.includes(requiredPermission)
|
||||
} catch (error) {
|
||||
console.error('解析权限数据失败:', error)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export function getRequiredPermission(method: string, url: string): string | string[] | null {
|
||||
|
||||
@@ -279,9 +279,9 @@ const formRules = {
|
||||
}
|
||||
|
||||
const roleDialogVisible = ref(false)
|
||||
const selectedRoles = ref<number[]>([])
|
||||
const allRoles = ref<{ key: number; label: string }[]>([])
|
||||
const currentUserId = ref<number | null>(null)
|
||||
const selectedRoles = ref<string[]>([])
|
||||
const allRoles = ref<{ key: string; label: string }[]>([])
|
||||
const currentUserId = ref<string | null>(null)
|
||||
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
|
||||
@@ -77,6 +77,7 @@ class TestBusinessFlow:
|
||||
"username": f"e2e_user_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"e2e_{unique_id}@example.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
@@ -176,6 +177,7 @@ class TestBusinessFlow:
|
||||
"username": f"admin_{unique_id}",
|
||||
"password": "Admin123!@#",
|
||||
"email": f"admin_{unique_id}@example.com",
|
||||
"phone": "13800138001",
|
||||
"status": 1
|
||||
}
|
||||
admin_user = await user_api.create_user(admin_user_data)
|
||||
@@ -186,6 +188,7 @@ class TestBusinessFlow:
|
||||
"username": f"regular_{unique_id}",
|
||||
"password": "User123!@#",
|
||||
"email": f"regular_{unique_id}@example.com",
|
||||
"phone": "13800138002",
|
||||
"status": 1
|
||||
}
|
||||
regular_user = await user_api.create_user(regular_user_data)
|
||||
@@ -238,6 +241,7 @@ class TestBusinessFlow:
|
||||
"username": f"cascade_user_{unique_id}_{i}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"cascade_{unique_id}_{i}@example.com",
|
||||
"phone": f"1380013800{i}",
|
||||
"status": 1
|
||||
}
|
||||
user_response = await user_api.create_user(user_data)
|
||||
@@ -282,6 +286,7 @@ class TestBusinessFlow:
|
||||
"username": f"search_{unique_id}_{i}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"search_{unique_id}_{i}@example.com",
|
||||
"phone": f"1380013800{i}",
|
||||
"status": 1
|
||||
}
|
||||
user_response = await user_api.create_user(user_data)
|
||||
@@ -323,6 +328,7 @@ class TestBusinessFlow:
|
||||
"username": f"recovery_{unique_id}",
|
||||
"password": "Valid123!@#",
|
||||
"email": f"recovery_{unique_id}@example.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ class TestRealE2E:
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": f"search_{timestamp}_{i}@example.com",
|
||||
"phone": f"1380013800{i}",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user