kindergarten_java/lesingle-edu-reading-platform-frontend/scripts/fetch-openapi.js
En 40589f59e7 chore: 重命名项目目录
前后端目录重命名:
- reading-platform-java/ → lesingle-edu-reading-platform-backend/
- reading-platform-frontend/ → lesingle-edu-reading-platform-frontend/

更新相关文件:
- 所有 shell 脚本中的目录引用
- pom.xml 和 application.yml 中的项目名称
- package.json 中的项目名称
- .claude/CLAUDE.md 中的路径引用
- README 文档中的路径引用
2026-03-26 11:31:47 +08:00

108 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 拉取 OpenAPI 文档并修复 SpringDoc 生成的 oneOf schema 问题
* 解决 orval 报错: "oneOf must match exactly one schema in oneOf"
*/
import { writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const TARGET = 'http://localhost:8480/v3/api-docs';
const OUTPUT = join(__dirname, '../openapi.json');
async function fetchAndFix() {
const res = await fetch(TARGET);
if (!res.ok) throw new Error(`Failed to fetch: ${res.status} ${res.statusText}. 请确保后端已启动 (mvn spring-boot:run)`);
let spec = await res.json();
const paths = spec.paths || {};
for (const path of Object.keys(paths)) {
let newKey = path.replace(/\/v1\/v1\//g, '/v1/');
if (newKey === path) newKey = path.replace(/^\/api\/(?!v1\/)/, '/api/v1/');
if (newKey !== path) {
paths[newKey] = paths[path];
delete paths[path];
}
}
fixOneOfInPaths(paths);
inlineResultObjectArrayRef(paths);
// 移除非法 schema 名 ResultObject[](含 [] 不符合 OpenAPI 规范)
if (spec.components?.schemas) {
delete spec.components.schemas['ResultObject[]'];
}
// 修复成长记录 images 字段:统一为 array of string避免 SpringDoc 误生成为 string
fixGrowthRecordImagesSchema(spec.components?.schemas);
writeFileSync(OUTPUT, JSON.stringify(spec, null, 2));
console.log('OpenAPI spec written to:', OUTPUT);
}
function fixOneOfInPaths(paths) {
for (const pathObj of Object.values(paths)) {
for (const op of Object.values(pathObj)) {
const res200 = op?.responses?.['200'];
if (!res200?.content) continue;
for (const media of Object.values(res200.content)) {
if (media?.schema) fixSchema(media.schema);
}
}
}
}
function fixSchema(schema) {
if (!schema || typeof schema !== 'object') return;
if (schema.oneOf && Array.isArray(schema.oneOf)) {
schema.type = 'array';
schema.items = { type: 'object', additionalProperties: true };
delete schema.oneOf;
}
if (schema.properties) {
for (const p of Object.values(schema.properties)) fixSchema(p);
}
if (schema.items) fixSchema(schema.items);
}
/** 将 GrowthRecordCreateRequest/GrowthRecordUpdateRequest 的 images 统一为 array of string */
function fixGrowthRecordImagesSchema(schemas) {
if (!schemas) return;
const arrayOfString = {
type: 'array',
items: { type: 'string', description: '图片 URL' },
description: '图片 URL 列表',
};
for (const name of ['GrowthRecordCreateRequest', 'GrowthRecordUpdateRequest']) {
const s = schemas[name];
if (s?.properties?.images?.type === 'string') {
schemas[name].properties.images = arrayOfString;
}
}
}
function inlineResultObjectArrayRef(paths) {
const inlineSchema = {
type: 'object',
properties: {
code: { type: 'integer', format: 'int32' },
message: { type: 'string' },
data: { type: 'array', items: { type: 'object', additionalProperties: true } },
},
};
for (const pathObj of Object.values(paths)) {
for (const op of Object.values(pathObj)) {
const res200 = op?.responses?.['200'];
if (!res200?.content) continue;
for (const media of Object.values(res200.content)) {
if (media?.schema?.['$ref']?.endsWith('ResultObject[]')) {
media.schema = { ...inlineSchema };
}
}
}
}
}
fetchAndFix().catch((e) => {
console.error(e);
process.exit(1);
});