swagger_generator_flutter/lib/parsers/swagger_data_parser.dart

413 lines
12 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

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/string_utils.dart';
import '../utils/type_validator.dart';
/// Swagger数据解析器
/// 负责解析Swagger JSON文档并提取相关信息
class SwaggerDataParser {
final CacheManager _cacheManager;
final PerformanceMonitor _performanceMonitor;
final TypeValidator _typeValidator;
// 缓存解析结果
SwaggerDocument? _cachedDocument;
SwaggerDataParser()
: _cacheManager = CacheManager(),
_performanceMonitor = PerformanceMonitor(),
_typeValidator = TypeValidator();
/// 获取并解析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? ?? '';
// 解析其他基本信息
final host = jsonData['host'] as String? ?? '';
final basePath = jsonData['basePath'] as String? ?? '/';
final schemes = List<String>.from(jsonData['schemes'] ?? ['https']);
final consumes = List<String>.from(jsonData['consumes'] ?? []);
final produces = List<String>.from(jsonData['produces'] ?? []);
// 解析tags信息 (用于获取控制器描述)
final tagsInfo = _parseTagsInfo(jsonData);
// 解析API路径
final paths = _parseApiPaths(jsonData);
// 解析API模型
final models = _parseApiModels(jsonData);
// 解析API控制器 (传入tags信息)
final controllers = _parseApiControllers(paths, tagsInfo);
final document = SwaggerDocument(
title: title,
version: version,
description: description,
host: host,
basePath: basePath,
schemes: schemes,
consumes: consumes,
produces: produces,
paths: paths,
models: models,
controllers: controllers,
);
// 缓存结果
_cacheManager.put(cacheKey, document);
_cachedDocument = document;
return document;
});
}
/// 解析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, ApiModel> _parseApiModels(Map<String, dynamic> jsonData) {
final models = <String, ApiModel>{};
// 优先解析 components/schemas (Swagger 3.0)
final schemas = jsonData['components']?['schemas'] as Map<String, dynamic>?;
// 如果没有 components/schemas尝试解析 definitions (Swagger 2.0)
final definitions = jsonData['definitions'] as Map<String, dynamic>?;
final modelDefinitions = schemas ?? definitions;
if (modelDefinitions == null) {
print(' 未发现模型定义 (components/schemas 或 definitions)');
return models;
}
print(
'🔍 发现模型定义位置: ${schemas != null ? 'components/schemas' : 'definitions'}',
);
try {
modelDefinitions.forEach((name, definition) {
final model = ApiModel.fromJson(
name,
definition as Map<String, dynamic>,
);
models[name] = model;
});
} catch (e) {
throw SwaggerParseException('解析API模型失败', details: e.toString());
}
return models;
}
/// 解析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,
};
}
}