From db70a3a6a8e21c9406680be0c34b44b3ca957c53 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 Jul 2026 13:46:20 +0800 Subject: [PATCH] feat(admin): AuditLogs + RuntimeLogs views + RBAC role management --- frontend-admin/src/api/admin.ts | 79 +++++- frontend-admin/src/components/Sidebar.vue | 10 +- frontend-admin/src/router/index.ts | 16 +- frontend-admin/src/types/index.ts | 17 +- frontend-admin/src/views/AuditLogs.vue | 297 +++++++++++++++++++++ frontend-admin/src/views/MfaManage.vue | 4 +- frontend-admin/src/views/Roles.vue | 141 +++++++++- frontend-admin/src/views/RuntimeLogs.vue | 303 ++++++++++++++++++++++ frontend-admin/src/views/SessionAudit.vue | 2 +- frontend-admin/src/views/SystemLogs.vue | 115 +++++++- 10 files changed, 957 insertions(+), 27 deletions(-) create mode 100644 frontend-admin/src/views/AuditLogs.vue create mode 100644 frontend-admin/src/views/RuntimeLogs.vue diff --git a/frontend-admin/src/api/admin.ts b/frontend-admin/src/api/admin.ts index c354b87..d108580 100644 --- a/frontend-admin/src/api/admin.ts +++ b/frontend-admin/src/api/admin.ts @@ -5,6 +5,21 @@ // 所有函数返回 axios response,调用方从 response 获取业务数据 import apiClient from './index' +import axios from 'axios' + +// 运行期日志下载使用独立 axios 实例:响应为 blob(文件流), +// 不能走 apiClient 的 JSON 信封拦截器(会把 blob 当成 {code,message,data} 解析而报错)。 +const downloadClient = axios.create({ + baseURL: '/api', + timeout: 30000, +}) +downloadClient.interceptors.request.use((config) => { + const token = localStorage.getItem('admin_token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config +}) import type { DashboardOverview, ConfigGroup, @@ -29,6 +44,7 @@ import type { RoleRevokeRequest, RoleMappingRule, RoleMappingRuleRequest, + EmployeeSearchResponse, } from '@/types' // ========================================================================== @@ -387,6 +403,13 @@ export function assignRole( return apiClient.post('/admin/roles/assign', data) } +/** 员工目录搜索(分配角色时按姓名/账号自动补全) */ +export function searchEmployees( + q: string +): Promise<{ data: { code: number; data: EmployeeSearchResponse; message: string } }> { + return apiClient.get('/admin/roles/employees/search', { params: { q } }) +} + /** 撤销用户角色 */ export function revokeRole( data: RoleRevokeRequest @@ -454,14 +477,65 @@ export function getAgentPerformance(params?: { // P2: 系统日志 // ========================================================================== -/** 获取系统日志(配置变更日志) */ +/** 获取系统日志(配置变更历史),支持配置键/操作人/时间范围筛选 */ export function getSystemLogs(params?: { page?: number page_size?: number + config_key?: string + changed_by?: string + from?: string + to?: string }): Promise<{ data: { code: number; data: { items: any[]; total: number; page: number; page_size: number }; message: string } }> { return apiClient.get('/admin/system-logs', { params }) } +// ========================================================================== +// 安全审计日志 (B) — 复用既有后端 GET /admin/audit-logs +// ========================================================================== + +/** 获取安全审计日志(分页 + 操作人/操作类型/时间范围筛选) */ +export function getAuditLogs(params?: { + employee_id?: string + action?: string + from?: string + to?: string + page?: number + page_size?: number +}): Promise<{ data: { code: number; data: { items: any[]; total: number; page: number; page_size: number }; message: string } }> { + return apiClient.get('/admin/audit-logs', { params }) +} + +// ========================================================================== +// 运行期日志 (D) — 新增后端 GET /admin/runtime-logs +// ========================================================================== + +/** 获取运行期日志(分页 + 级别/时间/关键字筛选) */ +export function getRuntimeLogs(params?: { + level?: string + from?: string + to?: string + keyword?: string + page?: number + page_size?: number +}): Promise<{ data: { code: number; data: { items: any[]; total: number; page: number; page_size: number }; message: string } }> { + return apiClient.get('/admin/runtime-logs', { params }) +} + +/** 下载运行期日志(命中行文本,blob 流,responseType: blob) */ +export function getRuntimeLogDownload(params?: { + level?: string + from?: string + to?: string + keyword?: string +}): Promise { + return downloadClient + .get('/admin/runtime-logs', { + params: { ...params, download: true }, + responseType: 'blob', + }) + .then((res) => res.data as Blob) +} + // ========================================================================== // P2: 满意度评价统计 // ========================================================================== @@ -633,4 +707,7 @@ export const adminApi = { getAgentPerformance, getSystemLogs, getEvaluationStats, + getAuditLogs, + getRuntimeLogs, + getRuntimeLogDownload, } diff --git a/frontend-admin/src/components/Sidebar.vue b/frontend-admin/src/components/Sidebar.vue index d2b3510..bef9a89 100644 --- a/frontend-admin/src/components/Sidebar.vue +++ b/frontend-admin/src/components/Sidebar.vue @@ -97,7 +97,15 @@ - 系统日志 + 配置变更历史 + + + + 安全审计日志 + + + + 运行期日志 diff --git a/frontend-admin/src/router/index.ts b/frontend-admin/src/router/index.ts index 0bf6fd9..c8f62f6 100644 --- a/frontend-admin/src/router/index.ts +++ b/frontend-admin/src/router/index.ts @@ -116,7 +116,21 @@ const routes = [ path: 'system-logs', name: 'SystemLogs', component: () => import('@/views/SystemLogs.vue'), - meta: { title: '系统日志', requiresAuth: true }, + meta: { title: '配置变更历史', requiresAuth: true }, + }, + { + // 安全审计日志(复用既有后端 GET /api/admin/audit-logs) + path: 'audit-logs', + name: 'AuditLogs', + component: () => import('@/views/AuditLogs.vue'), + meta: { title: '安全审计日志', requiresAuth: true }, + }, + { + // 运行期日志(新增后端 GET /api/admin/runtime-logs) + path: 'runtime-logs', + name: 'RuntimeLogs', + component: () => import('@/views/RuntimeLogs.vue'), + meta: { title: '运行期日志', requiresAuth: true }, }, { // P2 占位页:主题模板 diff --git a/frontend-admin/src/types/index.ts b/frontend-admin/src/types/index.ts index 44711ee..1b16ceb 100644 --- a/frontend-admin/src/types/index.ts +++ b/frontend-admin/src/types/index.ts @@ -433,6 +433,7 @@ export interface Role { export interface UserRole { id: string employee_id: string // 企微 UserID + employee_name?: string // 员工姓名(可选,后端 JOIN 返回) role_id: string // 关联角色 ID role_name: string // 角色标识 role_display_name: string // 角色显示名称 @@ -474,9 +475,23 @@ export const MAPPING_SOURCE_LABELS: Record = { ehr_position: 'eHR 岗位', } +/** 员工目录候选(搜索自动补全用) */ +export interface EmployeeCandidate { + employee_id: string + name: string + department: string +} + +/** 员工搜索响应 */ +export interface EmployeeSearchResponse { + items: EmployeeCandidate[] + full_directory: boolean | null // true=企微全组织;false=仅已登录员工;null=未搜索 +} + /** 分配角色请求 */ export interface RoleAssignRequest { - employee_id: string + target?: string // 员工账号或姓名(推荐,后端自动解析) + employee_id?: string // 兼容旧字段:直接传企微 UserID role_name: string reason?: string } diff --git a/frontend-admin/src/views/AuditLogs.vue b/frontend-admin/src/views/AuditLogs.vue new file mode 100644 index 0000000..2629bfc --- /dev/null +++ b/frontend-admin/src/views/AuditLogs.vue @@ -0,0 +1,297 @@ + + + + + diff --git a/frontend-admin/src/views/MfaManage.vue b/frontend-admin/src/views/MfaManage.vue index 0dbde6e..3b2c6cb 100644 --- a/frontend-admin/src/views/MfaManage.vue +++ b/frontend-admin/src/views/MfaManage.vue @@ -33,7 +33,7 @@
- +