feat(admin): 优化后台运维与任务管理
完善平台、基准模型、定价规则和实时负载的紧凑查询与滚动展示,并固定关键操作列。 新增管理员任务记录、批量计费结算和用户组限流、额度及模型权限维护能力,同时补充任务脱敏、查询索引与 OpenAPI 契约。 验证:pnpm openapi、Go 全量测试、pnpm lint、pnpm test、pnpm build、gofmt 与差异格式检查均通过。
This commit is contained in:
+98
-7
@@ -1,8 +1,9 @@
|
||||
import type { AdminSection, ApiDocSection, PageKey, PlaygroundMode, WorkspaceSection, WorkspaceTaskQuery } from './types';
|
||||
import type { AdminSection, AdminTaskQuery, ApiDocSection, PageKey, PlaygroundMode, WorkspaceSection, WorkspaceTaskQuery } from './types';
|
||||
|
||||
export interface AppRouteState {
|
||||
activePage: PageKey;
|
||||
adminSection: AdminSection;
|
||||
adminTaskQuery: AdminTaskQuery;
|
||||
apiDocSection: ApiDocSection;
|
||||
playgroundMode: PlaygroundMode;
|
||||
workspaceSection: WorkspaceSection;
|
||||
@@ -12,6 +13,7 @@ export interface AppRouteState {
|
||||
export const defaultRouteState: AppRouteState = {
|
||||
activePage: 'home',
|
||||
adminSection: 'overview',
|
||||
adminTaskQuery: defaultAdminTaskQuery(),
|
||||
apiDocSection: 'openApiOverview',
|
||||
playgroundMode: 'chat',
|
||||
workspaceSection: 'overview',
|
||||
@@ -31,14 +33,15 @@ const adminPaths: Record<AdminSection, string> = {
|
||||
globalModels: '/admin/providers',
|
||||
baseModels: '/admin/base-models',
|
||||
pricing: '/admin/pricing',
|
||||
billingSettlements: '/admin/billing-settlements',
|
||||
platforms: '/admin/platforms',
|
||||
realtimeLoad: '/admin/realtime-load',
|
||||
tasks: '/admin/tasks',
|
||||
tenants: '/admin/tenants',
|
||||
users: '/admin/users',
|
||||
userGroups: '/admin/user-groups',
|
||||
auditLogs: '/admin/audit-logs',
|
||||
runtime: '/admin/runtime',
|
||||
accessRules: '/admin/access-rules',
|
||||
systemSettings: '/admin/system-settings',
|
||||
};
|
||||
|
||||
@@ -82,19 +85,20 @@ export function parseAppRoute(input = currentLocationPath()): AppRouteState {
|
||||
const url = new URL(input, origin);
|
||||
const path = normalizePath(url.pathname);
|
||||
const workspaceTaskQuery = parseWorkspaceTaskQuery(url.searchParams);
|
||||
const adminTaskQuery = parseAdminTaskQuery(url.searchParams);
|
||||
if (path === '/') return { ...defaultRouteState };
|
||||
if (path.startsWith('/playground')) {
|
||||
return { ...defaultRouteState, activePage: 'playground', playgroundMode: parsePlaygroundMode(path), workspaceTaskQuery };
|
||||
return { ...defaultRouteState, activePage: 'playground', playgroundMode: parsePlaygroundMode(path), workspaceTaskQuery, adminTaskQuery };
|
||||
}
|
||||
if (path === '/models') return { ...defaultRouteState, activePage: 'models', workspaceTaskQuery };
|
||||
if (path === '/models') return { ...defaultRouteState, activePage: 'models', workspaceTaskQuery, adminTaskQuery };
|
||||
if (path.startsWith('/workspace')) {
|
||||
return { ...defaultRouteState, activePage: 'workspace', workspaceSection: parseWorkspaceSection(path), workspaceTaskQuery };
|
||||
return { ...defaultRouteState, activePage: 'workspace', workspaceSection: parseWorkspaceSection(path), workspaceTaskQuery, adminTaskQuery };
|
||||
}
|
||||
if (path.startsWith('/admin')) {
|
||||
return { ...defaultRouteState, activePage: 'admin', adminSection: parseAdminSection(path), workspaceTaskQuery };
|
||||
return { ...defaultRouteState, activePage: 'admin', adminSection: parseAdminSection(path), workspaceTaskQuery, adminTaskQuery };
|
||||
}
|
||||
if (path.startsWith('/docs')) {
|
||||
return { ...defaultRouteState, activePage: 'docs', apiDocSection: parseDocSection(path), workspaceTaskQuery };
|
||||
return { ...defaultRouteState, activePage: 'docs', apiDocSection: parseDocSection(path), workspaceTaskQuery, adminTaskQuery };
|
||||
}
|
||||
return { ...defaultRouteState };
|
||||
}
|
||||
@@ -130,6 +134,28 @@ export function pathForWorkspaceTaskQuery(query: WorkspaceTaskQuery) {
|
||||
return `${workspacePaths.tasks}${suffix ? `?${suffix}` : ''}`;
|
||||
}
|
||||
|
||||
export function pathForAdminTaskQuery(query: AdminTaskQuery) {
|
||||
const normalized = normalizeAdminTaskQuery(query);
|
||||
const search = new URLSearchParams();
|
||||
if (normalized.query) search.set('q', normalized.query);
|
||||
if (normalized.tenantId) search.set('tenantId', normalized.tenantId);
|
||||
if (normalized.userId) search.set('userId', normalized.userId);
|
||||
if (normalized.userGroupId) search.set('userGroupId', normalized.userGroupId);
|
||||
if (normalized.status) search.set('status', normalized.status);
|
||||
if (normalized.platformId) search.set('platformId', normalized.platformId);
|
||||
if (normalized.model) search.set('model', normalized.model);
|
||||
if (normalized.modelType) search.set('modelType', normalized.modelType);
|
||||
if (normalized.runMode) search.set('runMode', normalized.runMode);
|
||||
if (normalized.billingStatus) search.set('billingStatus', normalized.billingStatus);
|
||||
if (normalized.apiKey) search.set('apiKey', normalized.apiKey);
|
||||
if (normalized.createdFrom) search.set('createdFrom', normalized.createdFrom);
|
||||
if (normalized.createdTo) search.set('createdTo', normalized.createdTo);
|
||||
if (normalized.page !== 1) search.set('page', String(normalized.page));
|
||||
if (normalized.pageSize !== 20) search.set('pageSize', String(normalized.pageSize));
|
||||
const suffix = search.toString();
|
||||
return `${adminPaths.tasks}${suffix ? `?${suffix}` : ''}`;
|
||||
}
|
||||
|
||||
export function pathForAdminSection(section: AdminSection) {
|
||||
return adminPaths[section] ?? adminPaths.overview;
|
||||
}
|
||||
@@ -150,6 +176,7 @@ function parseAdminSection(path: string): AdminSection {
|
||||
if (path === '/admin') return 'overview';
|
||||
if (path === '/admin/models/global') return 'baseModels';
|
||||
if (path === '/admin/usergroups') return 'userGroups';
|
||||
if (path === '/admin/access-rules') return 'userGroups';
|
||||
return adminSections[path] ?? 'overview';
|
||||
}
|
||||
|
||||
@@ -207,6 +234,70 @@ export function workspaceTaskQueryKey(query: WorkspaceTaskQuery) {
|
||||
return JSON.stringify(normalized);
|
||||
}
|
||||
|
||||
export function defaultAdminTaskQuery(): AdminTaskQuery {
|
||||
return {
|
||||
query: '',
|
||||
tenantId: '',
|
||||
userId: '',
|
||||
userGroupId: '',
|
||||
status: '',
|
||||
platformId: '',
|
||||
model: '',
|
||||
modelType: '',
|
||||
runMode: '',
|
||||
billingStatus: '',
|
||||
apiKey: '',
|
||||
createdFrom: '',
|
||||
createdTo: '',
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeAdminTaskQuery(query: AdminTaskQuery): AdminTaskQuery {
|
||||
return {
|
||||
query: query.query.trim(),
|
||||
tenantId: query.tenantId.trim(),
|
||||
userId: query.userId.trim(),
|
||||
userGroupId: query.userGroupId.trim(),
|
||||
status: query.status.trim(),
|
||||
platformId: query.platformId.trim(),
|
||||
model: query.model.trim(),
|
||||
modelType: query.modelType.trim(),
|
||||
runMode: query.runMode.trim(),
|
||||
billingStatus: query.billingStatus.trim(),
|
||||
apiKey: query.apiKey.trim(),
|
||||
createdFrom: query.createdFrom.trim(),
|
||||
createdTo: query.createdTo.trim(),
|
||||
page: positiveInt(query.page, 1),
|
||||
pageSize: Math.min(100, Math.max(1, positiveInt(query.pageSize, 20))),
|
||||
};
|
||||
}
|
||||
|
||||
export function adminTaskQueryKey(query: AdminTaskQuery) {
|
||||
return JSON.stringify(normalizeAdminTaskQuery(query));
|
||||
}
|
||||
|
||||
function parseAdminTaskQuery(search: URLSearchParams): AdminTaskQuery {
|
||||
return normalizeAdminTaskQuery({
|
||||
query: search.get('q') ?? search.get('query') ?? '',
|
||||
tenantId: search.get('tenantId') ?? '',
|
||||
userId: search.get('userId') ?? '',
|
||||
userGroupId: search.get('userGroupId') ?? '',
|
||||
status: search.get('status') ?? '',
|
||||
platformId: search.get('platformId') ?? '',
|
||||
model: search.get('model') ?? '',
|
||||
modelType: search.get('modelType') ?? '',
|
||||
runMode: search.get('runMode') ?? '',
|
||||
billingStatus: search.get('billingStatus') ?? '',
|
||||
apiKey: search.get('apiKey') ?? '',
|
||||
createdFrom: search.get('createdFrom') ?? search.get('from') ?? '',
|
||||
createdTo: search.get('createdTo') ?? search.get('to') ?? '',
|
||||
page: Number(search.get('page') ?? 1),
|
||||
pageSize: Number(search.get('pageSize') ?? 20),
|
||||
});
|
||||
}
|
||||
|
||||
function parseWorkspaceTaskQuery(search: URLSearchParams): WorkspaceTaskQuery {
|
||||
return normalizeWorkspaceTaskQuery({
|
||||
query: search.get('q') ?? search.get('query') ?? '',
|
||||
|
||||
Reference in New Issue
Block a user