import { menuRoutes, miniApps, users, USER_STATUS, type MockMiniApp, } from './data' export interface MockContext { url: string params: string[] query: Record body: Record } export interface MockRule { method: 'GET' | 'POST' | 'PUT' | 'DELETE' pattern: RegExp handler: (ctx: MockContext) => unknown /** 全局 mock 关闭时仍生效(用于单独 mock 新增接口) */ alwaysMock?: boolean } const toNumber = (value: unknown, fallback = 0) => { const n = Number(value) return Number.isFinite(n) ? n : fallback } const pickString = (value: unknown, fallback = '') => (typeof value === 'string' ? value : fallback) export const mockRules: MockRule[] = [ { method: 'POST', pattern: /^\/api\/v1\/auth\/login$/, handler: (ctx) => ({ accessToken: 'mock-access-token', refreshToken: 'mock-refresh-token', tokenType: 'Bearer', expiresIn: 7200, username: pickString(ctx.body.username, 'admin'), }), }, { method: 'POST', pattern: /^\/api\/v1\/auth\/logout$/, handler: () => true, }, { method: 'GET', pattern: /^\/api\/v1\/auth\/me$/, handler: () => ({ userId: 1, username: 'admin', nickname: '超级管理员', avatar: '', roles: ['ADMIN'], permissions: [ 'sys:user:add', 'sys:user:edit', 'sys:user:delete', 'sys:user:query', 'miniapp:add', 'miniapp:edit', 'miniapp:delete', 'miniapp:query', ], }), }, { method: 'GET', pattern: /^\/api\/v1\/auth\/routes$/, handler: () => menuRoutes, }, { method: 'GET', pattern: /^\/api\/v1\/api\/admin\/users$/, handler: (ctx) => { const page = toNumber(ctx.query.page, 1) const size = toNumber(ctx.query.size, 20) const miniId = ctx.query.mini_id ? toNumber(ctx.query.mini_id) : 0 const keyword = (ctx.query.keyword as string) || '' const filtered = users.filter((item) => { if (miniId && item.mini_id !== miniId) return false if (keyword && !item.nickname.includes(keyword) && !item.openid.includes(keyword)) return false return true }) const start = (page - 1) * size return { list: filtered.slice(start, start + size), total: filtered.length } }, }, { method: 'GET', pattern: /^\/api\/v1\/api\/admin\/users\/(\d+)$/, handler: (ctx) => users.find((item) => item.id === toNumber(ctx.params[0])) ?? null, }, { method: 'PUT', pattern: /^\/api\/v1\/api\/admin\/users\/(\d+)$/, handler: (ctx) => { const target = users.find((item) => item.id === toNumber(ctx.params[0])) if (!target) return false const body = ctx.body as Record if (typeof body.nickname === 'string') target.nickname = body.nickname if (typeof body.avatar === 'string') target.avatar = body.avatar if (typeof body.status !== 'undefined') target.status = toNumber(body.status) === 0 ? 0 : 1 target.updated_at = new Date().toISOString() return target }, }, { method: 'POST', pattern: /^\/api\/v1\/api\/admin\/users\/(\d+)\/status$/, handler: (ctx) => { const target = users.find((item) => item.id === toNumber(ctx.params[0])) if (!target) return false const body = ctx.body as { status?: number } target.status = toNumber(body.status) === 0 ? 0 : 1 target.updated_at = new Date().toISOString() return target }, }, { method: 'GET', pattern: /^\/api\/v1\/api\/admin\/miniapps$/, handler: (ctx) => { const { status } = ctx.query const pageNum = toNumber(ctx.query.pageNum, 1) const pageSize = toNumber(ctx.query.pageSize, 10) const filtered = miniApps.filter((item) => !status || String(item.status) === status) const start = (pageNum - 1) * pageSize const list = filtered.slice(start, start + pageSize).map((item) => { const { app_secret: _app_secret, ...rest } = item return rest }) return { list, total: filtered.length } }, }, { method: 'GET', pattern: /^\/api\/v1\/api\/admin\/miniapps\/(\d+)$/, handler: (ctx) => { const target = miniApps.find((item) => item.id === toNumber(ctx.params[0])) if (!target) return null const { app_secret: _app_secret, ...rest } = target return rest }, }, { method: 'POST', pattern: /^\/api\/v1\/api\/admin\/miniapps$/, handler: (ctx) => { const id = miniApps.length + 1 const created: MockMiniApp = { id, name: pickString(ctx.body.name), description: pickString(ctx.body.description), app_id: pickString(ctx.body.app_id), app_secret: pickString(ctx.body.app_secret), status: toNumber(ctx.body.status, USER_STATUS.ENABLED) === 0 ? 0 : 1, ext: pickString(ctx.body.ext, '{}'), created_at: new Date().toISOString(), updated_at: new Date().toISOString(), } miniApps.unshift(created) return id }, }, { method: 'PUT', pattern: /^\/api\/v1\/api\/admin\/miniapps\/(\d+)$/, handler: (ctx) => { const target = miniApps.find((item) => item.id === toNumber(ctx.params[0])) if (!target) return false const body = ctx.body as Record if (typeof body.name === 'string') target.name = body.name if (typeof body.description === 'string') target.description = body.description if (typeof body.app_id === 'string') target.app_id = body.app_id if (typeof body.app_secret === 'string' && body.app_secret) target.app_secret = body.app_secret if (typeof body.status !== 'undefined') target.status = toNumber(body.status) === 0 ? 0 : 1 if (typeof body.ext === 'string') target.ext = body.ext target.updated_at = new Date().toISOString() return true }, }, { method: 'DELETE', pattern: /^\/api\/v1\/api\/admin\/miniapps\/([\d,]+)$/, handler: (ctx) => { const ids = ctx.params[0].split(',').map((id) => toNumber(id)) ids.forEach((id) => { const index = miniApps.findIndex((item) => item.id === id) if (index > -1) miniApps.splice(index, 1) }) return true }, }, { // 统计每个小程序的用户数(有真实用户的取真实值,其余返回稳定模拟值) method: 'GET', pattern: /^\/api\/v1\/api\/admin\/dashboard\/user-count-by-miniapp$/, alwaysMock: true, handler: () => miniApps.map((app) => { const real = users.filter((item) => item.mini_id === app.id).length return { miniAppId: app.id, miniAppName: app.name, userCount: real > 0 ? real : ((app.id * 37) % 200) + 8, } }), }, { method: 'GET', pattern: /^\/api\/v1\/api\/admin\/monitor\/server-info$/, alwaysMock: true, handler: () => ({ hostname: 'portal-server-01', os: 'Linux', osVersion: 'Ubuntu 22.04.3 LTS', arch: 'amd64', kernel: '5.15.0-91-generic', cpuModel: 'Intel(R) Xeon(R) Platinum 8369B CPU @ 2.70GHz', cpuCores: 8, runtimeVersion: 'go1.21.5', uptime: 1234560, }), }, { // 实时状态:每次请求生成浮动指标,模拟监控采集中 method: 'GET', pattern: /^\/api\/v1\/api\/admin\/monitor\/server-status$/, alwaysMock: true, handler: () => { const cpuUsage = Math.round((20 + Math.random() * 60) * 10) / 10 const memTotal = 16 * 1024 ** 3 const memUsed = Math.round(memTotal * (0.4 + Math.random() * 0.3)) const loadBase = (cpuUsage / 100) * 8 const gb = 1024 ** 3 const disks = [ { mount: '/', filesystem: '/dev/vda1', total: 100 * gb, used: 62 * gb, free: 38 * gb }, { mount: '/data', filesystem: '/dev/vdb1', total: 500 * gb, used: 311 * gb, free: 189 * gb }, { mount: '/logs', filesystem: '/dev/vdc1', total: 50 * gb, used: 9 * gb, free: 41 * gb }, ].map((disk) => ({ ...disk, usagePercent: Math.round((disk.used / disk.total) * 1000) / 10, })) return { cpu: { usagePercent: cpuUsage, cores: 8, model: 'Intel(R) Xeon(R) Platinum 8369B CPU @ 2.70GHz', loadAverage: [ Math.round((loadBase + Math.random()) * 100) / 100, Math.round((loadBase + Math.random()) * 100) / 100, Math.round((loadBase + Math.random()) * 100) / 100, ], }, memory: { total: memTotal, used: memUsed, free: memTotal - memUsed, usagePercent: Math.round((memUsed / memTotal) * 1000) / 10, }, disks, timestamp: Date.now(), } }, }, ]