430 lines
12 KiB
Dart
430 lines
12 KiB
Dart
import 'dart:convert';
|
||
import 'dart:io';
|
||
|
||
import 'package:http/http.dart' as http;
|
||
|
||
import '../core/config.dart';
|
||
import '../core/exceptions.dart';
|
||
import '../core/models.dart';
|
||
import '../utils/cache_manager.dart';
|
||
import '../utils/performance_monitor.dart';
|
||
import '../utils/reference_resolver.dart';
|
||
import '../utils/string_utils.dart';
|
||
|
||
/// Swagger数据解析器
|
||
/// 负责解析Swagger JSON文档并提取相关信息
|
||
class SwaggerDataParser {
|
||
final CacheManager _cacheManager;
|
||
final PerformanceMonitor _performanceMonitor;
|
||
|
||
// 缓存解析结果
|
||
SwaggerDocument? _cachedDocument;
|
||
|
||
SwaggerDataParser()
|
||
: _cacheManager = CacheManager(),
|
||
_performanceMonitor = PerformanceMonitor();
|
||
|
||
/// 获取并解析Swagger JSON文档
|
||
Future<SwaggerDocument> fetchAndParseSwaggerDocument() async {
|
||
// 如果有缓存且未过期,直接返回缓存结果
|
||
if (_cachedDocument != null) {
|
||
return _cachedDocument!;
|
||
}
|
||
|
||
return _performanceMonitor.measure(
|
||
'fetchAndParseSwaggerDocument',
|
||
() async {
|
||
try {
|
||
print('🔄 正在获取Swagger JSON文档...');
|
||
|
||
Map<String, dynamic> jsonData;
|
||
|
||
if (SwaggerConfig.swaggerJsonUrl.startsWith('file://')) {
|
||
// 处理本地文件
|
||
final filePath =
|
||
SwaggerConfig.swaggerJsonUrl.replaceFirst('file://', '');
|
||
final file = File(filePath);
|
||
if (await file.exists()) {
|
||
final content = await file.readAsString();
|
||
jsonData = json.decode(content) as Map<String, dynamic>;
|
||
} else {
|
||
throw SwaggerParseException(
|
||
'本地文件不存在',
|
||
url: SwaggerConfig.swaggerJsonUrl,
|
||
details: '文件路径: $filePath',
|
||
);
|
||
}
|
||
} else {
|
||
// 处理远程URL
|
||
final response = await http.get(
|
||
Uri.parse(SwaggerConfig.swaggerJsonUrl),
|
||
headers: SwaggerConfig.httpHeaders,
|
||
);
|
||
|
||
if (response.statusCode == 200) {
|
||
jsonData = json.decode(response.body) as Map<String, dynamic>;
|
||
} else {
|
||
throw SwaggerParseException(
|
||
'HTTP请求失败',
|
||
url: SwaggerConfig.swaggerJsonUrl,
|
||
statusCode: response.statusCode,
|
||
details: 'HTTP响应状态码: ${response.statusCode}',
|
||
);
|
||
}
|
||
}
|
||
|
||
_cachedDocument = await parseSwaggerDocument(jsonData);
|
||
print('✅ Swagger文档解析完成');
|
||
return _cachedDocument!;
|
||
} catch (e) {
|
||
if (e is SwaggerParseException) {
|
||
rethrow;
|
||
}
|
||
throw SwaggerParseException(
|
||
'获取Swagger文档失败',
|
||
url: SwaggerConfig.swaggerJsonUrl,
|
||
details: e.toString(),
|
||
);
|
||
}
|
||
},
|
||
);
|
||
}
|
||
|
||
/// 解析Swagger JSON文档
|
||
Future<SwaggerDocument> parseSwaggerDocument(
|
||
Map<String, dynamic> jsonData,
|
||
) async {
|
||
return _performanceMonitor.measure('parseSwaggerDocument', () async {
|
||
// 尝试从缓存获取
|
||
final cacheKey = 'swagger_doc_${jsonData.hashCode}';
|
||
final cachedResult = _cacheManager.get<SwaggerDocument>(cacheKey);
|
||
if (cachedResult != null) {
|
||
_cachedDocument = cachedResult;
|
||
return cachedResult;
|
||
}
|
||
|
||
// 解析文档基本信息
|
||
final info = jsonData['info'] as Map<String, dynamic>? ?? {};
|
||
final title = info['title'] as String? ?? 'API Documentation';
|
||
final version = info['version'] as String? ?? '1.0.0';
|
||
final description = info['description'] as String? ?? '';
|
||
|
||
// 解析 servers (OpenAPI 3.0)
|
||
final servers = _parseServers(jsonData);
|
||
|
||
// 解析 components (OpenAPI 3.0)
|
||
final components = _parseComponents(jsonData);
|
||
|
||
// 解析tags信息 (用于获取控制器描述)
|
||
final tagsInfo = _parseTagsInfo(jsonData);
|
||
|
||
// 解析API路径
|
||
final paths = _parseApiPaths(jsonData);
|
||
|
||
// 解析API模型 (从 components 中提取)
|
||
final models = components.schemas;
|
||
|
||
// 解析API控制器 (传入tags信息)
|
||
final controllers = _parseApiControllers(paths, tagsInfo);
|
||
|
||
final document = SwaggerDocument(
|
||
title: title,
|
||
version: version,
|
||
description: description,
|
||
servers: servers,
|
||
components: components,
|
||
paths: paths,
|
||
models: models,
|
||
controllers: controllers,
|
||
);
|
||
|
||
// 缓存结果
|
||
_cacheManager.put(cacheKey, document);
|
||
_cachedDocument = document;
|
||
|
||
return document;
|
||
});
|
||
}
|
||
|
||
/// 解析 servers 配置 (OpenAPI 3.0)
|
||
List<ApiServer> _parseServers(Map<String, dynamic> jsonData) {
|
||
final servers = <ApiServer>[];
|
||
|
||
try {
|
||
final serversJson = jsonData['servers'] as List<dynamic>?;
|
||
if (serversJson != null) {
|
||
for (final serverJson in serversJson) {
|
||
if (serverJson is Map<String, dynamic>) {
|
||
final server = ApiServer.fromJson(serverJson);
|
||
servers.add(server);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果没有 servers 配置,提供默认值
|
||
if (servers.isEmpty) {
|
||
servers.add(const ApiServer(url: '/'));
|
||
}
|
||
} catch (e) {
|
||
print('⚠️ 解析servers配置时发生错误: $e');
|
||
// 提供默认服务器配置
|
||
servers.add(const ApiServer(url: '/'));
|
||
}
|
||
|
||
return servers;
|
||
}
|
||
|
||
/// 解析 components 配置 (OpenAPI 3.0)
|
||
ApiComponents _parseComponents(Map<String, dynamic> jsonData) {
|
||
try {
|
||
final componentsJson = jsonData['components'] as Map<String, dynamic>?;
|
||
if (componentsJson != null) {
|
||
// 使用引用解析器处理复杂嵌套和循环引用
|
||
final resolver = ReferenceResolver();
|
||
final resolvedSchemas = resolver.resolveModels(componentsJson);
|
||
|
||
// 创建 ApiComponents,但使用解析后的 schemas
|
||
final components = ApiComponents.fromJson(componentsJson);
|
||
return ApiComponents(
|
||
schemas: resolvedSchemas,
|
||
responses: components.responses,
|
||
parameters: components.parameters,
|
||
examples: components.examples,
|
||
requestBodies: components.requestBodies,
|
||
headers: components.headers,
|
||
securitySchemes: components.securitySchemes,
|
||
links: components.links,
|
||
callbacks: components.callbacks,
|
||
);
|
||
}
|
||
} catch (e) {
|
||
print('⚠️ 解析components配置时发生错误: $e');
|
||
}
|
||
|
||
return const ApiComponents();
|
||
}
|
||
|
||
/// 解析tags信息
|
||
Map<String, String> _parseTagsInfo(Map<String, dynamic> jsonData) {
|
||
final tagsInfo = <String, String>{};
|
||
|
||
try {
|
||
final tags = jsonData['tags'] as List<dynamic>?;
|
||
if (tags != null) {
|
||
for (final tag in tags) {
|
||
if (tag is Map<String, dynamic>) {
|
||
final name = tag['name'] as String?;
|
||
final description = tag['description'] as String?;
|
||
if (name != null && description != null) {
|
||
tagsInfo[name] = description;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
print('⚠️ 解析tags信息时发生错误: $e');
|
||
}
|
||
|
||
return tagsInfo;
|
||
}
|
||
|
||
/// 解析API路径
|
||
Map<String, ApiPath> _parseApiPaths(Map<String, dynamic> jsonData) {
|
||
final paths = <String, ApiPath>{};
|
||
final pathsData = jsonData['paths'] as Map<String, dynamic>?;
|
||
|
||
if (pathsData == null) {
|
||
throw SwaggerParseException('未发现API路径定义');
|
||
}
|
||
|
||
try {
|
||
pathsData.forEach((pathKey, pathValue) {
|
||
if (pathValue is Map<String, dynamic>) {
|
||
pathValue.forEach((methodKey, methodValue) {
|
||
if (methodValue is Map<String, dynamic>) {
|
||
final method = HttpMethod.fromString(methodKey);
|
||
final apiPath = ApiPath.fromJson(
|
||
pathKey,
|
||
methodKey, // 传入字符串而不是HttpMethod对象
|
||
methodValue,
|
||
);
|
||
final key =
|
||
'${method.value.toUpperCase()}_${pathKey.replaceAll('/', '_')}';
|
||
paths[key] = apiPath;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
} catch (e) {
|
||
throw SwaggerParseException('解析API路径失败', details: e.toString());
|
||
}
|
||
|
||
return paths;
|
||
}
|
||
|
||
/// 解析API控制器
|
||
Map<String, ApiController> _parseApiControllers(
|
||
Map<String, ApiPath> paths,
|
||
Map<String, String> tagsInfo,
|
||
) {
|
||
final controllers = <String, List<ApiPath>>{};
|
||
|
||
try {
|
||
// 根据tags分组API路径
|
||
for (final apiPath in paths.values) {
|
||
for (final tag in apiPath.tags) {
|
||
controllers.putIfAbsent(tag, () => []).add(apiPath);
|
||
}
|
||
}
|
||
|
||
// 创建控制器对象
|
||
final result = <String, ApiController>{};
|
||
controllers.forEach((name, pathList) {
|
||
// 从tags信息中获取描述
|
||
final swaggerDescription = tagsInfo[name];
|
||
|
||
// 使用通用的描述获取方法
|
||
final description = SwaggerConfig.getControllerDescription(
|
||
name,
|
||
swaggerDescription: swaggerDescription,
|
||
);
|
||
|
||
result[name] = ApiController(
|
||
name: name,
|
||
description: description,
|
||
paths: pathList,
|
||
);
|
||
});
|
||
|
||
return result;
|
||
} catch (e) {
|
||
throw SwaggerParseException('解析API控制器失败', details: e.toString());
|
||
}
|
||
}
|
||
|
||
/// 解析属性类型
|
||
String parsePropertyType(Map<String, dynamic> propData) {
|
||
try {
|
||
// 直接类型
|
||
if (propData['type'] != null) {
|
||
return propData['type'] as String;
|
||
}
|
||
|
||
// 引用类型 ($ref)
|
||
if (propData['\$ref'] != null) {
|
||
final ref = propData['\$ref'] as String;
|
||
// 从 #/components/schemas/ModelName 或 #/definitions/ModelName 中提取类型名
|
||
final parts = ref.split('/');
|
||
if (parts.isNotEmpty) {
|
||
return parts.last;
|
||
}
|
||
}
|
||
|
||
// 数组类型
|
||
if (propData['items'] != null) {
|
||
final items = propData['items'] as Map<String, dynamic>;
|
||
final itemType = parsePropertyType(items);
|
||
return 'array<$itemType>';
|
||
}
|
||
|
||
// 默认类型
|
||
return 'string';
|
||
} catch (e) {
|
||
throw SwaggerParseException('解析属性类型失败', details: e.toString());
|
||
}
|
||
}
|
||
|
||
/// 获取Dart类型映射
|
||
String getDartType(String swaggerType, String? format) {
|
||
switch (swaggerType.toLowerCase()) {
|
||
case 'string':
|
||
switch (format?.toLowerCase()) {
|
||
case 'date':
|
||
case 'date-time':
|
||
return 'DateTime';
|
||
case 'byte':
|
||
case 'binary':
|
||
return 'String';
|
||
default:
|
||
return 'String';
|
||
}
|
||
case 'integer':
|
||
switch (format?.toLowerCase()) {
|
||
case 'int64':
|
||
return 'int';
|
||
case 'int32':
|
||
default:
|
||
return 'int';
|
||
}
|
||
case 'number':
|
||
switch (format?.toLowerCase()) {
|
||
case 'float':
|
||
case 'double':
|
||
return 'double';
|
||
default:
|
||
return 'double';
|
||
}
|
||
case 'boolean':
|
||
return 'bool';
|
||
case 'array':
|
||
return 'List<dynamic>';
|
||
case 'object':
|
||
return 'Map<String, dynamic>';
|
||
case 'file':
|
||
return 'String';
|
||
default:
|
||
// 检查是否为数组类型
|
||
if (swaggerType.startsWith('array<') && swaggerType.endsWith('>')) {
|
||
final itemType = swaggerType.substring(6, swaggerType.length - 1);
|
||
final dartItemType = getDartType(itemType, null);
|
||
return 'List<$dartItemType>';
|
||
}
|
||
|
||
// 默认为自定义类型
|
||
return StringUtils.generateClassName(swaggerType);
|
||
}
|
||
}
|
||
|
||
/// 清除缓存
|
||
void clearCache() {
|
||
_cachedDocument = null;
|
||
_cacheManager.clear();
|
||
}
|
||
|
||
/// 获取文档统计信息
|
||
Map<String, dynamic> getDocumentStats() {
|
||
if (_cachedDocument == null) {
|
||
return {};
|
||
}
|
||
|
||
final doc = _cachedDocument!;
|
||
|
||
// 统计HTTP方法
|
||
final methodStats = <String, int>{};
|
||
for (final path in doc.paths.values) {
|
||
final method = path.method.value;
|
||
methodStats[method] = (methodStats[method] ?? 0) + 1;
|
||
}
|
||
|
||
// 统计模型类型
|
||
final modelStats = <String, int>{};
|
||
for (final model in doc.models.values) {
|
||
if (model.isEnum) {
|
||
modelStats['enum'] = (modelStats['enum'] ?? 0) + 1;
|
||
} else {
|
||
modelStats['class'] = (modelStats['class'] ?? 0) + 1;
|
||
}
|
||
}
|
||
|
||
return {
|
||
'title': doc.title,
|
||
'version': doc.version,
|
||
'paths': doc.paths.length,
|
||
'models': doc.models.length,
|
||
'controllers': doc.controllers.length,
|
||
'methods': methodStats,
|
||
'modelTypes': modelStats,
|
||
};
|
||
}
|
||
}
|