refactor(user): 调整用户 ID 类型和添加 phone 字段

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