import { execFileSync } from 'node:child_process'; import { mkdirSync, writeFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const openApiUrl = process.env.OPENAPI_URL || 'http://localhost:5090/openapi/v1.json'; const response = await fetch(openApiUrl); if (!response.ok) throw new Error(`OpenAPI 下载失败:${response.status} ${response.statusText}`); const document = await response.json(); const generatedDir = path.join(projectRoot, '.generated'); const apiDir = path.join(projectRoot, 'src', 'api'); mkdirSync(generatedDir, { recursive: true }); mkdirSync(apiDir, { recursive: true }); const snapshotPath = path.join(generatedDir, 'openapi.json'); writeFileSync(snapshotPath, `${JSON.stringify(document)}\n`); const cli = path.join(projectRoot, 'node_modules', '.bin', process.platform === 'win32' ? 'openapi-typescript.cmd' : 'openapi-typescript'); execFileSync(cli, [snapshotPath, '-o', path.join(apiDir, 'schema.generated.d.ts')], { stdio: 'inherit' }); const methods = new Set(['get', 'post', 'put', 'patch', 'delete']); const operations = []; for (const [route, pathItem] of Object.entries(document.paths || {})) { for (const [method, operation] of Object.entries(pathItem || {})) { if (!methods.has(method)) continue; const tags = operation.tags || []; const isPlatform = route.startsWith('/api/platform-admin/') || route === '/api/platform-admin' || route.startsWith('/api/backoffice/platform/'); if (!isPlatform) continue; const requestSchema = operation.requestBody?.content?.['application/json']?.schema || null; const responseEntry = Object.entries(operation.responses || {}).find(([status]) => /^2/.test(status)); const responseSchema = responseEntry?.[1]?.content?.['application/json']?.schema || null; operations.push({ id: `${method.toUpperCase()} ${route}`, method: method.toUpperCase(), path: route, tag: tags[0] || '平台端', summary: operation.summary || `${method.toUpperCase()} ${route}`, description: operation.description || '', parameters: [...(pathItem.parameters || []), ...(operation.parameters || [])], requestSchema, responseSchema, }); } } operations.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method)); const schemas = document.components?.schemas || {}; const source = `/* eslint-disable */\n// 由 scripts/generate-platform-operations.mjs 根据后端 OpenAPI 自动生成,请勿手改。\nimport type { PlatformOperation } from './types';\n\nexport const generatedAt = ${JSON.stringify(new Date().toISOString())};\nexport const platformOperations = ${JSON.stringify(operations, null, 2)} as const satisfies readonly PlatformOperation[];\nexport const openApiSchemas = ${JSON.stringify(schemas, null, 2)} as const;\n`; writeFileSync(path.join(apiDir, 'platform-operations.generated.ts'), source); console.log(`已生成 ${operations.length} 个平台接口契约,来源:${openApiUrl}`);