fix: 对齐租户端鉴权与后端菜单流程

将登录/用户信息/权限/菜单接口对齐到租户后端,补充默认租户头与令牌刷新逻辑,并强制使用后端菜单模式以保证登录后触发 /auth/menu 加载。
This commit is contained in:
2026-02-06 12:40:55 +08:00
parent 09b73076da
commit a1935f4008
9 changed files with 175 additions and 73 deletions

View File

@@ -1,20 +1,43 @@
import { baseRequestClient, requestClient } from '#/api/request';
export namespace AuthApi {
export interface CurrentUserProfile {
account?: string;
avatar?: string;
displayName?: string;
merchantId?: null | number | string;
permissions?: string[];
roles?: string[];
tenantId?: number | string;
userId?: number | string;
}
/** 登录接口参数 */
export interface LoginParams {
account?: string;
password?: string;
username?: string;
}
/** 登录接口返回值 */
export interface LoginResult {
accessToken: string;
accessToken?: string;
accessTokenExpiresAt?: string;
isNewUser?: boolean;
refreshToken?: string;
refreshTokenExpiresAt?: string;
user?: CurrentUserProfile;
}
export interface RefreshTokenResult {
data: string;
status: number;
export interface RefreshTokenParams {
refreshToken: string;
}
export interface ApiResponse<T> {
code: number;
data: T;
message?: string;
success: boolean;
}
}
@@ -22,30 +45,33 @@ export namespace AuthApi {
* 登录
*/
export async function loginApi(data: AuthApi.LoginParams) {
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
const account = data.account ?? data.username;
return requestClient.post<AuthApi.LoginResult>('/auth/login', {
account,
password: data.password,
});
}
/**
* 刷新accessToken
*/
export async function refreshTokenApi() {
return baseRequestClient.post<AuthApi.RefreshTokenResult>('/auth/refresh', {
withCredentials: true,
});
export async function refreshTokenApi(data: AuthApi.RefreshTokenParams) {
return baseRequestClient.post<AuthApi.ApiResponse<AuthApi.LoginResult>>(
'/auth/refresh',
data,
);
}
/**
* 退出登录
*/
export async function logoutApi() {
return baseRequestClient.post('/auth/logout', {
withCredentials: true,
});
return baseRequestClient.post('/auth/logout');
}
/**
* 获取用户权限码
*/
export async function getAccessCodesApi() {
return requestClient.get<string[]>('/auth/codes');
return requestClient.get<string[]>('/auth/permissions');
}

View File

@@ -6,5 +6,5 @@ import { requestClient } from '#/api/request';
* 获取用户所有菜单
*/
export async function getAllMenusApi() {
return requestClient.get<RouteRecordStringComponent[]>('/menu/all');
return requestClient.get<RouteRecordStringComponent[]>('/auth/menu');
}

View File

@@ -2,9 +2,46 @@ import type { UserInfo } from '@vben/types';
import { requestClient } from '#/api/request';
const TENANT_STORAGE_KEY = 'sys-tenant-id';
interface TenantUserProfile {
account?: string;
avatar?: string;
displayName?: string;
merchantId?: null | number | string;
permissions?: string[];
roles?: string[];
tenantId?: number | string;
userId?: number | string;
}
function normalizeId(value: null | number | string | undefined) {
if (value === null || value === undefined) {
return '';
}
return String(value);
}
function mapProfileToUserInfo(profile: TenantUserProfile): UserInfo {
return {
avatar: profile.avatar ?? '',
desc: '',
homePath: '',
realName: profile.displayName ?? profile.account ?? '',
roles: profile.roles ?? [],
token: '',
userId: normalizeId(profile.userId),
username: profile.account ?? '',
};
}
/**
* 获取用户信息
*/
export async function getUserInfoApi() {
return requestClient.get<UserInfo>('/user/info');
const profile = await requestClient.get<TenantUserProfile>('/auth/profile');
if (profile.tenantId !== null && profile.tenantId !== undefined) {
localStorage.setItem(TENANT_STORAGE_KEY, String(profile.tenantId));
}
return mapProfileToUserInfo(profile);
}