diff --git a/tenant-platform/src/api/license.ts b/tenant-platform/src/api/license.ts index af63bfb..b8a4e23 100644 --- a/tenant-platform/src/api/license.ts +++ b/tenant-platform/src/api/license.ts @@ -97,6 +97,18 @@ export interface LicenseDevice { createdAt: string } +export interface LicenseOperationLog { + id: string + appKey: string + operator: string + action: string + resourceType: string + resourceId?: string | null + summary?: string | null + detailJson?: string | null + createdAt: string +} + export const licenseApi = { getAppLicense(appKey: string) { return licenseClient.get<{ data: { license: AppLicense; devices: LicenseDevice[] } }>( @@ -118,4 +130,10 @@ export const licenseApi = { reactivateDevice(id: string) { return licenseClient.put<{ data: null }>(`/api/license/admin/devices/${encodeURIComponent(id)}/reactivate`) }, + + getOperationLogs(appKey: string, page = 0, size = 20) { + return licenseClient.get<{ data: { content: LicenseOperationLog[]; total: number; totalPages: number } }>( + `/api/license/admin/operation-logs`, { params: { appKey, page, size } }, + ) + }, } diff --git a/tenant-platform/src/api/push.ts b/tenant-platform/src/api/push.ts index d26c54e..69d8008 100644 --- a/tenant-platform/src/api/push.ts +++ b/tenant-platform/src/api/push.ts @@ -55,6 +55,18 @@ export interface TestPushResult { targets: DeviceInfo[] } +export interface PushOperationLog { + id: string + appKey: string + operator: string + action: string + resourceType: string + resourceId?: string | null + summary?: string | null + detail?: string | null + createdAt: string +} + export const pushAdminApi = { getUserStatus(appKey: string, userId: string) { return client.get<{ data: UserPushStatus }>('/push/admin/user-status', { @@ -77,4 +89,10 @@ export const pushAdminApi = { payload: payload ?? null, }) }, + + getOperationLogs(appKey: string, page = 0, size = 20) { + return client.get<{ data: { content: PushOperationLog[]; total: number; totalPages: number } }>( + '/push/admin/operation-logs', { params: { appKey, page, size } }, + ) + }, } diff --git a/tenant-platform/src/views/logs/OperationLogView.vue b/tenant-platform/src/views/logs/OperationLogView.vue index b66d5f1..8d80888 100644 --- a/tenant-platform/src/views/logs/OperationLogView.vue +++ b/tenant-platform/src/views/logs/OperationLogView.vue @@ -207,6 +207,68 @@ + + + +
+ + + + 刷新 +
+ +
+ + + + + + + + + + + + +
+ + +
+ + + +
+ + + + 刷新 +
+ +
+ + + + + + + + + + + + +
+ + +
@@ -218,9 +280,11 @@ import { appApi, type App } from '@/api/app' import { updateAdminApi, type OperationLog as UpdateOperationLog } from '@/api/update' import { operationLogApi, type TenantOperationLog } from '@/api/operationLog' import { imAdminApi, type OperationLog as ImOperationLog } from '@/api/im' +import { pushAdminApi, type PushOperationLog } from '@/api/push' +import { licenseApi, type LicenseOperationLog } from '@/api/license' import { formatTime } from '@/utils/date' -const activeSource = ref<'TENANT' | 'IM' | 'UPDATE'>('TENANT') +const activeSource = ref<'TENANT' | 'IM' | 'UPDATE' | 'PUSH' | 'LICENSE'>('TENANT') // ── 租户平台 state ─────────────────────────────────────────────────────────── @@ -248,6 +312,24 @@ const updateLimit = ref(100) const apps = ref([]) const updateAppKey = ref('') +// ── 推送 state ───────────────────────────────────────────────────────────── + +const pushLoading = ref(false) +const pushLogs = ref([]) +const pushTotal = ref(0) +const pushPage = ref(0) +const pushPageSize = ref(20) +const pushAppKey = ref('') + +// ── 授权 state ───────────────────────────────────────────────────────────── + +const licenseLoading = ref(false) +const licenseLogs = ref([]) +const licenseTotal = ref(0) +const licensePage = ref(0) +const licensePageSize = ref(20) +const licenseAppKey = ref('') + // ── 初始化 ────────────────────────────────────────────────────────────────── onMounted(async () => { @@ -255,11 +337,13 @@ onMounted(async () => { }) watch(activeSource, async (value) => { - if ((value === 'UPDATE' || value === 'IM') && !apps.value.length) { + if (['UPDATE', 'IM', 'PUSH', 'LICENSE'].includes(value) && !apps.value.length) { await loadApps() } if (value === 'UPDATE') await loadUpdateLogs() if (value === 'IM' && imAppKey.value) await loadImLogs() + if (value === 'PUSH' && pushAppKey.value) await loadPushLogs() + if (value === 'LICENSE' && licenseAppKey.value) await loadLicenseLogs() }) // ── 数据加载 ──────────────────────────────────────────────────────────────── @@ -271,6 +355,8 @@ async function loadApps() { apps.value = res.data.data if (!updateAppKey.value && apps.value.length) updateAppKey.value = apps.value[0].appKey if (!imAppKey.value && apps.value.length) imAppKey.value = apps.value[0].appKey + if (!pushAppKey.value && apps.value.length) pushAppKey.value = apps.value[0].appKey + if (!licenseAppKey.value && apps.value.length) licenseAppKey.value = apps.value[0].appKey } catch { /* ignore */ } } @@ -314,6 +400,32 @@ async function loadUpdateLogs() { } } +async function loadPushLogs() { + if (!pushAppKey.value) { pushLogs.value = []; return } + pushLoading.value = true + try { + const res = await pushAdminApi.getOperationLogs(pushAppKey.value, pushPage.value, pushPageSize.value) + const data = res.data.data + pushLogs.value = data.content ?? [] + pushTotal.value = data.total ?? 0 + } finally { + pushLoading.value = false + } +} + +async function loadLicenseLogs() { + if (!licenseAppKey.value) { licenseLogs.value = []; return } + licenseLoading.value = true + try { + const res = await licenseApi.getOperationLogs(licenseAppKey.value, licensePage.value, licensePageSize.value) + const data = res.data.data + licenseLogs.value = data.content ?? [] + licenseTotal.value = data.total ?? 0 + } finally { + licenseLoading.value = false + } +} + // ── 事件处理 ──────────────────────────────────────────────────────────────── function handleTenantPageChange(nextPage: number) { @@ -340,6 +452,26 @@ function handleUpdateAppChange() { loadUpdateLogs() } +function handlePushAppChange() { + pushPage.value = 0 + loadPushLogs() +} + +function handlePushPageChange(nextPage: number) { + pushPage.value = nextPage - 1 + loadPushLogs() +} + +function handleLicenseAppChange() { + licensePage.value = 0 + loadLicenseLogs() +} + +function handleLicensePageChange(nextPage: number) { + licensePage.value = nextPage - 1 + loadLicenseLogs() +} + // ── 通用标签映射 ──────────────────────────────────────────────────────────── function moduleLabel(moduleType: string): string { @@ -468,6 +600,16 @@ function actionLabel(action: string): string { STORE_SUBMIT_STORE_STAGE: '单市场提交进度', STORE_SUBMIT_ALREADY_LIVE: '市场已上架', STORE_WITHDRAW_BEFORE_RESUBMIT: '撤回后重新提交', + // ── push-service: 推送管理 ── + UPDATE_USER: '编辑用户信息', + UPDATE_USER_STATUS: '变更用户状态', + DELETE_USER: '删除用户', + IMPORT_USER: '导入用户', + TEST_PUSH: '发送测试推送', + // ── license-service: 授权管理 ── + UPDATE_LICENSE: '更新授权配置', + REVOKE_DEVICE: '吊销设备授权', + REACTIVATE_DEVICE: '重新激活设备', } return map[action] ?? action } @@ -513,6 +655,11 @@ function resourceTypeLabel(resourceType: string): string { GRAY_MEMBER: '灰度成员', STORE_SUBMIT: '市场提交', SERVICE: '服务配置', + // push-service + PUSH: '推送消息', + // license-service + LICENSE: '授权许可', + DEVICE: '设备', }[resourceType] ?? resourceType } @@ -533,6 +680,26 @@ function generateImSummary(row: ImOperationLog): string { return `${act}(${res})` } +// ── 推送摘要 ──────────────────────────────────────────────────────────────── + +function pushSummaryText(row: PushOperationLog): string { + if (row.summary) return row.summary + const act = actionLabel(row.action) + const res = resourceTypeLabel(row.resourceType) + if (row.resourceId) return `${act} — ${res} ${row.resourceId}` + return `${act}(${res})` +} + +// ── 授权摘要 ──────────────────────────────────────────────────────────────── + +function licenseSummaryText(row: LicenseOperationLog): string { + if (row.summary) return row.summary + const act = actionLabel(row.action) + const res = resourceTypeLabel(row.resourceType) + if (row.resourceId) return `${act} — ${res} ${row.resourceId}` + return `${act}(${res})` +} + // ── 版本管理摘要 ──────────────────────────────────────────────────────────── function updateSummaryText(row: UpdateOperationLog): string {