Files
Web_BAI_Manage_ApiServer/.tmp-openapi-validate/add-openapi-examples.js
XuJiacheng e47060f54f feat: 添加微信认证相关接口,包括微信登录和用户资料更新功能
- 新增 wechatLogin 接口,支持使用微信临时凭证 code 登录或注册用户,返回用户信息和 auth token。
- 新增 wechatProfile 接口,支持更新微信用户资料,支持非空字段增量更新。
- 定义相关请求和响应的 schema,包括 WechatLoginRequest、WechatProfileRequest、AuthSuccessResponse 和 WechatProfileResponse。
- 处理不同的响应状态码,包括成功、参数错误、认证失败等。
2026-04-08 09:04:36 +08:00

269 lines
7.7 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.
const fs = require('fs');
const path = require('path');
const YAML = require('yaml');
const repoRoot = process.cwd();
const rootFile = path.join(repoRoot, 'pocket-base', 'spec', 'openapi-wx.yaml');
const splitDir = path.join(repoRoot, 'pocket-base', 'spec', 'openapi-wx');
const filePaths = [
rootFile,
...fs
.readdirSync(splitDir)
.filter((name) => name.endsWith('.yaml'))
.map((name) => path.join(splitDir, name)),
];
const docs = new Map(
filePaths.map((filePath) => [filePath, YAML.parse(fs.readFileSync(filePath, 'utf8'))]),
);
function decodePointerSegment(segment) {
return segment.replace(/~1/g, '/').replace(/~0/g, '~');
}
function getByPointer(documentData, pointer) {
if (!pointer || pointer === '#' || pointer === '') {
return documentData;
}
const segments = pointer
.replace(/^#/, '')
.split('/')
.filter(Boolean)
.map(decodePointerSegment);
let current = documentData;
for (const segment of segments) {
if (current == null) {
return undefined;
}
current = current[segment];
}
return current;
}
function resolveRef(ref, currentFile) {
const [filePart, hashPart = ''] = ref.split('#');
const targetFile = filePart
? path.resolve(path.dirname(currentFile), filePart)
: currentFile;
const targetDoc = docs.get(targetFile);
if (!targetDoc) {
return { targetFile, targetKey: `${targetFile}#${hashPart}`, schema: undefined };
}
const pointer = hashPart ? `#${hashPart}` : '#';
return {
targetFile,
targetKey: `${targetFile}${pointer}`,
schema: getByPointer(targetDoc, pointer),
};
}
function pickType(typeValue) {
if (Array.isArray(typeValue)) {
return typeValue.find((item) => item !== 'null') || typeValue[0];
}
return typeValue;
}
function cleanLabelText(text, fallback) {
if (!text || typeof text !== 'string') {
return fallback;
}
const normalized = text
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
.map((line) => line.replace(/^[\-\d\.\)\s]+/, '').trim())
.find((line) => line && !/^(可选|必填|说明|注意)[。:]?$/u.test(line));
if (!normalized) {
return fallback;
}
return normalized.replace(/[`"'<>]/g, '').trim() || fallback;
}
function placeholderKey(key) {
return typeof key === 'string' && key.includes('|');
}
function deepMerge(baseValue, nextValue) {
if (
baseValue &&
nextValue &&
typeof baseValue === 'object' &&
typeof nextValue === 'object' &&
!Array.isArray(baseValue) &&
!Array.isArray(nextValue)
) {
const merged = { ...baseValue };
const nextKeys = Object.keys(nextValue);
const hasConcreteNextKeys = nextKeys.some((key) => !placeholderKey(key));
if (hasConcreteNextKeys) {
for (const key of Object.keys(merged)) {
if (placeholderKey(key)) {
delete merged[key];
}
}
}
for (const [key, value] of Object.entries(nextValue)) {
if (key in merged) {
merged[key] = deepMerge(merged[key], value);
} else {
merged[key] = value;
}
}
return merged;
}
return nextValue;
}
function buildLabel(schemaLike, fallback) {
return cleanLabelText(schemaLike?.description || schemaLike?.title, fallback);
}
function fallbackScalar(label, fieldType) {
return `${label}|${fieldType || 'string'}`;
}
function buildExample(schemaLike, currentFile, fieldName = 'field', seenRefs = new Set()) {
if (schemaLike == null) {
return fallbackScalar(fieldName, 'string');
}
if (typeof schemaLike !== 'object' || Array.isArray(schemaLike)) {
return fallbackScalar(fieldName, 'string');
}
if (schemaLike.$ref) {
const { targetFile, targetKey, schema } = resolveRef(schemaLike.$ref, currentFile);
if (!schema) {
return fallbackScalar(fieldName, 'string');
}
if (seenRefs.has(targetKey)) {
return fallbackScalar(fieldName, 'object');
}
const nextSeen = new Set(seenRefs);
nextSeen.add(targetKey);
return buildExample(schema, targetFile, fieldName, nextSeen);
}
if (schemaLike.allOf) {
return schemaLike.allOf.reduce((accumulator, item) => {
const nextValue = buildExample(item, currentFile, fieldName, seenRefs);
return deepMerge(accumulator, nextValue);
}, {});
}
if (schemaLike.oneOf && schemaLike.oneOf.length > 0) {
return buildExample(schemaLike.oneOf[0], currentFile, fieldName, seenRefs);
}
if (schemaLike.anyOf && schemaLike.anyOf.length > 0) {
return buildExample(schemaLike.anyOf[0], currentFile, fieldName, seenRefs);
}
const fieldType =
pickType(schemaLike.type) ||
(schemaLike.properties || schemaLike.additionalProperties ? 'object' : undefined) ||
(schemaLike.items ? 'array' : undefined) ||
'string';
const fieldLabel = buildLabel(schemaLike, fieldName);
if (fieldType === 'object') {
const exampleObject = {};
if (schemaLike.properties && typeof schemaLike.properties === 'object') {
for (const [propertyName, propertySchema] of Object.entries(schemaLike.properties)) {
exampleObject[propertyName] = buildExample(
propertySchema,
currentFile,
propertyName,
seenRefs,
);
}
}
if (Object.keys(exampleObject).length === 0 && schemaLike.additionalProperties) {
exampleObject[`${fieldLabel}字段|string`] = `${fieldLabel}值|string`;
}
if (Object.keys(exampleObject).length === 0) {
exampleObject[`${fieldLabel}|string`] = `${fieldLabel}|string`;
}
return exampleObject;
}
if (fieldType === 'array') {
const itemFieldName = fieldName.endsWith('s') ? fieldName.slice(0, -1) : `${fieldName}_item`;
return [buildExample(schemaLike.items || {}, currentFile, itemFieldName, seenRefs)];
}
return fallbackScalar(fieldLabel, fieldType);
}
function httpMethods(pathItem) {
return ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace'].filter(
(method) => pathItem && typeof pathItem[method] === 'object',
);
}
for (const filePath of filePaths.filter((file) => file !== rootFile)) {
const documentData = docs.get(filePath);
if (!documentData || typeof documentData !== 'object') {
continue;
}
if (documentData.components && documentData.components.schemas) {
for (const [schemaName, schemaValue] of Object.entries(documentData.components.schemas)) {
schemaValue.example = buildExample(schemaValue, filePath, schemaName, new Set());
}
}
if (documentData.paths) {
for (const pathItem of Object.values(documentData.paths)) {
for (const method of httpMethods(pathItem)) {
const operation = pathItem[method];
const requestContent = operation?.requestBody?.content;
if (requestContent && typeof requestContent === 'object') {
for (const media of Object.values(requestContent)) {
if (media && media.schema) {
delete media.examples;
media.example = buildExample(media.schema, filePath, 'request', new Set());
}
}
}
const responses = operation?.responses;
if (responses && typeof responses === 'object') {
for (const response of Object.values(responses)) {
const responseContent = response?.content;
if (!responseContent || typeof responseContent !== 'object') {
continue;
}
for (const media of Object.values(responseContent)) {
if (media && media.schema) {
delete media.examples;
media.example = buildExample(media.schema, filePath, 'response', new Set());
}
}
}
}
}
}
}
fs.writeFileSync(filePath, YAML.stringify(documentData, { lineWidth: 0 }), 'utf8');
}
console.log(`Updated examples in ${filePaths.length - 1} split OpenAPI files.`);