From f51aed1f0f054ef2f05d78cec5ebcd8744bc7518 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 13 Jul 2026 21:38:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=A1=E8=AE=A1=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E4=B8=AD=E6=96=87=E5=B1=95=E7=A4=BA=E4=B8=8E=E5=8A=A8?= =?UTF-8?q?=E4=BD=9C=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补全动作/业务类型中文映射,表格与下拉按分组展示,系统任务操作人可读。 --- .../admin/views/AdminAuditLogsView.vue | 234 ++++++++++++++---- frontend/src/shared/utils/auditLabels.ts | 120 +++++++++ 2 files changed, 310 insertions(+), 44 deletions(-) create mode 100644 frontend/src/shared/utils/auditLabels.ts diff --git a/frontend/src/features/admin/views/AdminAuditLogsView.vue b/frontend/src/features/admin/views/AdminAuditLogsView.vue index 686feee..5fbce73 100644 --- a/frontend/src/features/admin/views/AdminAuditLogsView.vue +++ b/frontend/src/features/admin/views/AdminAuditLogsView.vue @@ -3,6 +3,14 @@ import { Search } from '@element-plus/icons-vue' import { computed, onMounted, reactive, ref } from 'vue' import { fetchAdminAuditLogs, type AdminAuditLog } from '@/features/admin/api/adminAudit' +import { + AUDIT_BIZ_TYPE_OPTIONS, + auditActionLabel, + auditActionTagType, + auditBizTypeLabel, + groupedAuditActionOptions, + isHighRiskAuditAction, +} from '@/shared/utils/auditLabels' import { formatDateTime } from '@/shared/utils/time' import AdminTablePagination from '../components/AdminTablePagination.vue' @@ -18,10 +26,11 @@ const filters = reactive({ biz_type: '', }) +const actionGroups = groupedAuditActionOptions() + +/** 当前页高风险条数(筛选结果内的本页统计) */ const highRiskCount = computed( - () => - logs.value.filter(item => item.action.includes('freeze') || item.action.includes('update')) - .length + () => logs.value.filter(item => isHighRiskAuditAction(item.action)).length ) onMounted(loadLogs) @@ -54,20 +63,22 @@ async function handlePageChange() { await loadLogs() } +function queryLogs() { + currentPage.value = 1 + void loadLogs() +} + function actorName(row: AdminAuditLog) { - return row.actor_nickname || row.actor_username || `${row.actor_type} ${row.actor_id}` + if (row.actor_type === 'system' || row.actor_id === 0) { + return '系统任务' + } + return row.actor_nickname || row.actor_username || `管理员 ${row.actor_id}` } function detailText(row: AdminAuditLog | null) { if (!row?.detail) return '{}' return JSON.stringify(row.detail, null, 2) } - -function actionType(action: string) { - if (action.includes('freeze')) return 'danger' - if (action.includes('update') || action.includes('create')) return 'warning' - return 'info' -} + + diff --git a/frontend/src/shared/utils/auditLabels.ts b/frontend/src/shared/utils/auditLabels.ts new file mode 100644 index 0000000..97b03f3 --- /dev/null +++ b/frontend/src/shared/utils/auditLabels.ts @@ -0,0 +1,120 @@ +/** 审计日志动作 / 业务类型中文映射(展示用,存储仍为英文 code) */ + +export interface AuditOption { + value: string + label: string + /** 筛选下拉分组 */ + group?: string + /** 是否高风险操作 */ + highRisk?: boolean +} + +export const AUDIT_ACTION_OPTIONS: AuditOption[] = [ + // 用户 + { value: 'admin_user.freeze', label: '冻结用户', group: '用户', highRisk: true }, + { value: 'admin_user.unfreeze', label: '解冻用户', group: '用户' }, + { value: 'admin_user.set_deposit_free_quota', label: '设置免押额度', group: '用户', highRisk: true }, + { value: 'admin_user.manual_realname', label: '人工实名', group: '用户', highRisk: true }, + { value: 'admin_user.revoke_realname', label: '撤销实名', group: '用户', highRisk: true }, + { value: 'admin_user.wallet_adjust', label: '调整用户余额', group: '用户', highRisk: true }, + // 商品 + { value: 'listing.admin_offline', label: '后台下架商品', group: '商品', highRisk: true }, + { value: 'listing.mark_abnormal', label: '商品标记异常', group: '商品', highRisk: true }, + { value: 'listing.transfer_owner', label: '转移商品所有权', group: '商品', highRisk: true }, + { value: 'listing.adjust_review_price', label: '审核调价', group: '商品' }, + // 订单 + { value: 'order.admin_close', label: '客服关闭订单', group: '订单', highRisk: true }, + { value: 'order.admin_seal', label: '客服封存订单', group: '订单', highRisk: true }, + { value: 'order.mark_abnormal', label: '订单标记异常', group: '订单', highRisk: true }, + { value: 'order.reset_handoff', label: '重置交接流程', group: '订单' }, + { value: 'order.reset_renter_confirm', label: '重置租客确认', group: '订单' }, + { value: 'order.reset_checkout_confirm', label: '重置结账确认', group: '订单' }, + { value: 'order.reset_return_overdue', label: '重置归还逾期', group: '订单' }, + { value: 'order.deposit_hold', label: '押金暂扣', group: '订单', highRisk: true }, + { value: 'order.deposit_release', label: '押金归还', group: '订单', highRisk: true }, + // 超时任务(system) + { value: 'order.timeout.pending_payment', label: '超时关闭待支付', group: '系统任务' }, + { value: 'order.timeout.owner_submit', label: '超时号主提交交接', group: '系统任务' }, + { value: 'order.timeout.renter_confirm', label: '超时租客确认收号', group: '系统任务' }, + { value: 'order.timeout.return_overdue', label: '超时归还逾期', group: '系统任务' }, + { value: 'order.timeout.owner_checkout_confirm', label: '超时号主确认结账', group: '系统任务' }, + { value: 'chat.renter_retention.remove', label: '到期移除租客会话', group: '系统任务' }, + // 提号 + { value: 'pickup_create', label: '创建提号', group: '提号' }, + { value: 'pickup_complete', label: '完成提号结算', group: '提号', highRisk: true }, + { value: 'pickup_cancel', label: '取消提号', group: '提号' }, + // 申诉 + { value: 'dispute.arbitrate', label: '申诉仲裁', group: '申诉', highRisk: true }, + // 系统配置 + { value: 'system_config.create', label: '创建系统配置', group: '系统配置', highRisk: true }, + { value: 'system_config.update', label: '更新系统配置', group: '系统配置', highRisk: true }, + // 支付配置 + { value: 'payment_config.create', label: '创建支付配置', group: '支付配置', highRisk: true }, + { value: 'payment_config.update', label: '更新支付配置', group: '支付配置', highRisk: true }, + { value: 'payment_config.delete', label: '删除支付配置', group: '支付配置', highRisk: true }, + { value: 'payment_config.view_secret', label: '查看支付密钥', group: '支付配置', highRisk: true }, + { value: 'payment_config.export', label: '导出支付配置', group: '支付配置', highRisk: true }, + { value: 'payment_config.import', label: '导入支付配置', group: '支付配置', highRisk: true }, +] + +export const AUDIT_BIZ_TYPE_OPTIONS: AuditOption[] = [ + { value: 'user', label: '用户' }, + { value: 'listing', label: '商品' }, + { value: 'order', label: '订单' }, + { value: 'dispute', label: '申诉' }, + { value: 'system_config', label: '系统配置' }, + { value: 'payment_config', label: '支付配置' }, + { value: 'admin_pickup', label: '提号' }, +] + +const actionLabelMap = Object.fromEntries(AUDIT_ACTION_OPTIONS.map(item => [item.value, item.label])) +const bizTypeLabelMap = Object.fromEntries(AUDIT_BIZ_TYPE_OPTIONS.map(item => [item.value, item.label])) +const highRiskActions = new Set(AUDIT_ACTION_OPTIONS.filter(item => item.highRisk).map(item => item.value)) + +/** 动作 code → 中文;未知 code 原样返回 */ +export function auditActionLabel(action: string | undefined | null): string { + if (!action) return '-' + return actionLabelMap[action] || action +} + +/** 业务类型 code → 中文 */ +export function auditBizTypeLabel(bizType: string | undefined | null): string { + if (!bizType) return '-' + return bizTypeLabelMap[bizType] || bizType +} + +export function isHighRiskAuditAction(action: string | undefined | null): boolean { + if (!action) return false + if (highRiskActions.has(action)) return true + // 兜底:未登记但含敏感关键词 + return /freeze|delete|import|export|secret|wallet_adjust|seal|close|arbitrate|offline|abnormal/i.test( + action + ) +} + +/** 表格标签颜色 */ +export function auditActionTagType( + action: string | undefined | null +): 'danger' | 'warning' | 'success' | 'info' { + if (!action) return 'info' + if (isHighRiskAuditAction(action)) return 'danger' + if (action.startsWith('order.timeout') || action.startsWith('chat.')) return 'info' + if (action.includes('create') || action.includes('update') || action.includes('reset')) { + return 'warning' + } + if (action.includes('complete') || action.includes('release') || action.includes('unfreeze')) { + return 'success' + } + return 'info' +} + +/** 按 group 分组,供 el-option-group 使用 */ +export function groupedAuditActionOptions(): Array<{ group: string; options: AuditOption[] }> { + const map = new Map() + for (const item of AUDIT_ACTION_OPTIONS) { + const group = item.group || '其他' + if (!map.has(group)) map.set(group, []) + map.get(group)!.push(item) + } + return Array.from(map.entries()).map(([group, options]) => ({ group, options })) +}