import '../core/models.dart'; import '../utils/string_utils.dart'; import 'base_generator.dart'; /// 文档生成器 /// 负责生成API文档 class DocumentationGenerator extends BaseGenerator { final SwaggerDocument document; final bool includeExamples; final bool includeSchemas; final bool includeResponses; final String? customTitle; DocumentationGenerator( this.document, { this.includeExamples = true, this.includeSchemas = true, this.includeResponses = true, this.customTitle, }); @override String get generatorType => 'DocumentationGenerator'; @override String generate() { final buffer = StringBuffer(); // 生成文档头部 _generateHeader(buffer); // 生成目录 _generateTableOfContents(buffer); // 生成API概述 _generateApiOverview(buffer); // 生成认证信息 _generateAuthenticationInfo(buffer); // 生成API端点文档 _generateEndpointsDocumentation(buffer); // 生成数据模型文档 if (includeSchemas) { _generateSchemasDocumentation(buffer); } // 生成错误代码文档 _generateErrorCodesDocumentation(buffer); // 生成示例代码 if (includeExamples) { _generateExamplesDocumentation(buffer); } // 生成更新日志 _generateChangeLog(buffer); return generateTypeCheckedCode(buffer.toString()); } /// 生成文档头部 void _generateHeader(StringBuffer buffer) { final title = customTitle ?? document.title; buffer.writeln('# $title'); buffer.writeln(''); if (document.description.isNotEmpty) { buffer.writeln('${document.description}'); buffer.writeln(''); } buffer.writeln('**版本**: ${document.version}'); buffer.writeln('**基础URL**: ${_getBaseUrl()}'); buffer.writeln('**生成时间**: ${DateTime.now().toIso8601String()}'); buffer.writeln(''); // 生成徽章 buffer.writeln( '![API版本](https://img.shields.io/badge/API-${document.version}-blue.svg)'); buffer.writeln('![状态](https://img.shields.io/badge/状态-活跃-green.svg)'); buffer.writeln(''); } /// 生成目录 void _generateTableOfContents(StringBuffer buffer) { buffer.writeln('## 📋 目录'); buffer.writeln(''); buffer.writeln('- [API概述](#api概述)'); buffer.writeln('- [认证](#认证)'); buffer.writeln('- [API端点](#api端点)'); // 按控制器分组的端点 final controllerGroups = _groupPathsByController(); for (final controllerName in controllerGroups.keys) { final anchor = controllerName.toLowerCase().replaceAll(' ', '-'); buffer.writeln(' - [$controllerName](#$anchor)'); } if (includeSchemas) { buffer.writeln('- [数据模型](#数据模型)'); } buffer.writeln('- [错误代码](#错误代码)'); if (includeExamples) { buffer.writeln('- [示例代码](#示例代码)'); } buffer.writeln('- [更新日志](#更新日志)'); buffer.writeln(''); } /// 生成API概述 void _generateApiOverview(StringBuffer buffer) { buffer.writeln('## 🚀 API概述'); buffer.writeln(''); // 统计信息 final stats = _generateStats(); buffer.writeln('### 📊 统计信息'); buffer.writeln(''); buffer.writeln('- **总端点数**: ${stats['totalEndpoints']}'); buffer.writeln('- **控制器数**: ${stats['controllersCount']}'); buffer.writeln('- **数据模型数**: ${stats['modelsCount']}'); buffer.writeln(''); // HTTP方法统计 final methodStats = stats['methodStats'] as Map; buffer.writeln('### 🔗 HTTP方法分布'); buffer.writeln(''); for (final entry in methodStats.entries) { final method = entry.key; final count = entry.value; final percentage = ((count / stats['totalEndpoints']) * 100).toStringAsFixed(1); buffer.writeln('- **$method**: $count个 ($percentage%)'); } buffer.writeln(''); // 支持的格式 buffer.writeln('### 🌐 服务器配置'); buffer.writeln(''); if (document.servers.isNotEmpty) { for (final server in document.servers) { buffer.writeln('**服务器**: `${server.url}`'); if (server.description.isNotEmpty) { buffer.writeln('- ${server.description}'); } if (server.variables.isNotEmpty) { buffer.writeln('- 变量:'); server.variables.forEach((name, variable) { buffer.writeln( ' - `$name`: ${variable.description} (默认: ${variable.defaultValue})'); }); } buffer.writeln(''); } } else { buffer.writeln('**服务器**: 相对路径 `/`'); buffer.writeln(''); } } /// 生成认证信息 void _generateAuthenticationInfo(StringBuffer buffer) { buffer.writeln('## 🔐 认证'); buffer.writeln(''); buffer.writeln('本API使用以下认证方式:'); buffer.writeln(''); buffer.writeln('### Bearer Token'); buffer.writeln(''); buffer.writeln('在请求头中包含Authorization字段:'); buffer.writeln(''); buffer.writeln('```'); buffer.writeln('Authorization: Bearer YOUR_TOKEN_HERE'); buffer.writeln('```'); buffer.writeln(''); buffer.writeln('### 获取Token'); buffer.writeln(''); buffer.writeln('请使用登录接口获取访问令牌。'); buffer.writeln(''); } /// 生成端点文档 void _generateEndpointsDocumentation(StringBuffer buffer) { buffer.writeln('## 📡 API端点'); buffer.writeln(''); final controllerGroups = _groupPathsByController(); for (final entry in controllerGroups.entries) { final controllerName = entry.key; final paths = entry.value; buffer.writeln('### $controllerName'); buffer.writeln(''); // 按HTTP方法和路径排序 paths.sort((a, b) { final methodOrder = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; final aIndex = methodOrder.indexOf(a.method.value); final bIndex = methodOrder.indexOf(b.method.value); if (aIndex != bIndex) { return aIndex.compareTo(bIndex); } return a.path.compareTo(b.path); }); for (final path in paths) { _generateEndpointDocumentation(buffer, path); } buffer.writeln(''); } } /// 生成单个端点文档 void _generateEndpointDocumentation(StringBuffer buffer, ApiPath path) { // 端点标题 final title = path.summary.isNotEmpty ? path.summary : path.operationId; buffer.writeln('#### ${path.method.value} ${path.path}'); buffer.writeln(''); if (title.isNotEmpty) { buffer.writeln('**$title**'); buffer.writeln(''); } // 描述 if (path.description.isNotEmpty) { buffer.writeln(path.description); buffer.writeln(''); } // 标签 if (path.tags.isNotEmpty) { buffer.writeln('**标签**: ${path.tags.join(', ')}'); buffer.writeln(''); } // 参数 if (path.parameters.isNotEmpty) { buffer.writeln('**参数**:'); buffer.writeln(''); // 按参数位置分组 final paramGroups = >{}; for (final param in path.parameters) { paramGroups.putIfAbsent(param.location, () => []).add(param); } for (final entry in paramGroups.entries) { final location = entry.key; final params = entry.value; buffer.writeln('*${_getLocationName(location)}参数*:'); buffer.writeln(''); buffer.writeln('| 参数名 | 类型 | 必填 | 描述 | 示例 |'); buffer.writeln('|--------|------|------|------|------|'); for (final param in params) { final required = param.required ? '✅' : '❌'; final example = param.example?.toString() ?? '-'; final description = param.description.isNotEmpty ? param.description : '-'; buffer.writeln( '| ${param.name} | ${param.type.value} | $required | $description | $example |'); } buffer.writeln(''); } } // 响应 if (includeResponses && path.responses.isNotEmpty) { buffer.writeln('**响应**:'); buffer.writeln(''); for (final entry in path.responses.entries) { final code = entry.key; final response = entry.value; buffer.writeln('*HTTP $code*:'); if (response.description.isNotEmpty) { buffer.writeln('- ${response.description}'); } buffer.writeln(''); } } // 示例 if (includeExamples) { _generateEndpointExample(buffer, path); } buffer.writeln('---'); buffer.writeln(''); } /// 生成端点示例 void _generateEndpointExample(StringBuffer buffer, ApiPath path) { buffer.writeln('**示例**:'); buffer.writeln(''); // cURL示例 buffer.writeln('```bash'); buffer.write('curl -X ${path.method.value} '); buffer.write('${_getBaseUrl()}${path.path}'); if (path.parameters.any((p) => p.location == ParameterLocation.header)) { buffer.write(' \\'); buffer.writeln(''); buffer.write(' -H "Authorization: Bearer YOUR_TOKEN"'); } if (path.method == HttpMethod.post || path.method == HttpMethod.put) { buffer.write(' \\'); buffer.writeln(''); buffer.write(' -H "Content-Type: application/json"'); buffer.write(' \\'); buffer.writeln(''); buffer.write(' -d \'{"key": "value"}\''); } buffer.writeln(''); buffer.writeln('```'); buffer.writeln(''); // Dart示例 buffer.writeln('```dart'); buffer.writeln('import \'dart:convert\';'); buffer.writeln('import \'package:http/http.dart\' as http;'); buffer.writeln(''); buffer.writeln('class ApiClient {'); buffer.writeln(' static const String baseUrl = \'${_getBaseUrl()}\';'); buffer.writeln(' String? _token;'); buffer.writeln(''); buffer.writeln(' void setToken(String token) {'); buffer.writeln(' _token = token;'); buffer.writeln(' }'); buffer.writeln(''); buffer.writeln(' Map get _headers => {'); buffer.writeln(' \'Content-Type\': \'application/json\','); buffer.writeln( ' if (_token != null) \'Authorization\': \'Bearer \$_token\','); buffer.writeln(' };'); buffer.writeln(''); buffer .writeln(' Future> get(String endpoint) async {'); buffer.writeln(' final response = await http.get('); buffer.writeln(' Uri.parse(\'\$baseUrl\$endpoint\'),'); buffer.writeln(' headers: _headers,'); buffer.writeln(' );'); buffer.writeln(''); buffer.writeln(' if (response.statusCode == 200) {'); buffer.writeln(' return jsonDecode(response.body);'); buffer.writeln(' } else {'); buffer.writeln( ' throw Exception(\'Failed to load data: \${response.statusCode}\');'); buffer.writeln(' }'); buffer.writeln(' }'); buffer.writeln(''); buffer.writeln( ' Future> post(String endpoint, Map data) async {'); buffer.writeln(' final response = await http.post('); buffer.writeln(' Uri.parse(\'\$baseUrl\$endpoint\'),'); buffer.writeln(' headers: _headers,'); buffer.writeln(' body: jsonEncode(data),'); buffer.writeln(' );'); buffer.writeln(''); buffer.writeln( ' if (response.statusCode == 200 || response.statusCode == 201) {'); buffer.writeln(' return jsonDecode(response.body);'); buffer.writeln(' } else {'); buffer.writeln( ' throw Exception(\'Failed to post data: \${response.statusCode}\');'); buffer.writeln(' }'); buffer.writeln(' }'); buffer.writeln('}'); buffer.writeln('```'); buffer.writeln(''); } /// 生成数据模型文档 void _generateSchemasDocumentation(StringBuffer buffer) { if (document.models.isEmpty) return; buffer.writeln('## 📋 数据模型'); buffer.writeln(''); final sortedModels = document.models.values.toList() ..sort((a, b) => a.name.compareTo(b.name)); for (final model in sortedModels) { _generateModelDocumentation(buffer, model); } } /// 生成模型文档 void _generateModelDocumentation(StringBuffer buffer, ApiModel model) { buffer.writeln('### ${model.name}'); buffer.writeln(''); if (model.description.isNotEmpty) { buffer.writeln(model.description); buffer.writeln(''); } if (model.isEnum) { buffer.writeln('**枚举值**:'); buffer.writeln(''); for (final value in model.enumValues) { buffer.writeln('- `$value`'); } buffer.writeln(''); } else { buffer.writeln('**属性**:'); buffer.writeln(''); if (model.properties.isNotEmpty) { buffer.writeln('| 属性名 | 类型 | 必填 | 描述 |'); buffer.writeln('|--------|------|------|------|'); for (final entry in model.properties.entries) { final propName = entry.key; final prop = entry.value; final required = model.required.contains(propName) ? '✅' : '❌'; final type = _getPropertyTypeDescription(prop); final description = prop.description.isNotEmpty ? prop.description : '-'; buffer.writeln('| $propName | $type | $required | $description |'); } } buffer.writeln(''); } // JSON示例 if (includeExamples) { buffer.writeln('**JSON示例**:'); buffer.writeln(''); buffer.writeln('```json'); buffer.writeln(_generateModelExample(model)); buffer.writeln('```'); buffer.writeln(''); } buffer.writeln('---'); buffer.writeln(''); } /// 生成错误代码文档 void _generateErrorCodesDocumentation(StringBuffer buffer) { buffer.writeln('## ❌ 错误代码'); buffer.writeln(''); // 标准HTTP状态码 final errorCodes = { '400': '请求参数错误', '401': '未授权访问', '403': '禁止访问', '404': '资源不存在', '405': '方法不允许', '422': '参数验证失败', '500': '服务器内部错误', '502': '网关错误', '503': '服务不可用', }; buffer.writeln('| 状态码 | 描述 |'); buffer.writeln('|--------|------|'); for (final entry in errorCodes.entries) { buffer.writeln('| ${entry.key} | ${entry.value} |'); } buffer.writeln(''); // 错误响应格式 buffer.writeln('### 错误响应格式'); buffer.writeln(''); buffer.writeln('```json'); buffer.writeln('{'); buffer.writeln(' "error": {'); buffer.writeln(' "code": "ERROR_CODE",'); buffer.writeln(' "message": "错误描述",'); buffer.writeln(' "details": "详细信息"'); buffer.writeln(' }'); buffer.writeln('}'); buffer.writeln('```'); buffer.writeln(''); } /// 生成示例代码 void _generateExamplesDocumentation(StringBuffer buffer) { buffer.writeln('## 💡 示例代码'); buffer.writeln(''); // Dart HTTP客户端示例 buffer.writeln('### Dart HTTP客户端'); buffer.writeln(''); buffer.writeln('```dart'); buffer.writeln('import \'dart:convert\';'); buffer.writeln('import \'package:http/http.dart\' as http;'); buffer.writeln(''); buffer.writeln('class ApiClient {'); buffer.writeln(' static const String baseUrl = \'${_getBaseUrl()}\';'); buffer.writeln(' String? _token;'); buffer.writeln(''); buffer.writeln(' void setToken(String token) {'); buffer.writeln(' _token = token;'); buffer.writeln(' }'); buffer.writeln(''); buffer.writeln(' Map get _headers => {'); buffer.writeln(' \'Content-Type\': \'application/json\','); buffer.writeln( ' if (_token != null) \'Authorization\': \'Bearer \$_token\','); buffer.writeln(' };'); buffer.writeln(''); buffer .writeln(' Future> get(String endpoint) async {'); buffer.writeln(' final response = await http.get('); buffer.writeln(' Uri.parse(\'\$baseUrl\$endpoint\'),'); buffer.writeln(' headers: _headers,'); buffer.writeln(' );'); buffer.writeln(''); buffer.writeln(' if (response.statusCode == 200) {'); buffer.writeln(' return jsonDecode(response.body);'); buffer.writeln(' } else {'); buffer.writeln( ' throw Exception(\'Failed to load data: \${response.statusCode}\');'); buffer.writeln(' }'); buffer.writeln(' }'); buffer.writeln(''); buffer.writeln( ' Future> post(String endpoint, Map data) async {'); buffer.writeln(' final response = await http.post('); buffer.writeln(' Uri.parse(\'\$baseUrl\$endpoint\'),'); buffer.writeln(' headers: _headers,'); buffer.writeln(' body: jsonEncode(data),'); buffer.writeln(' );'); buffer.writeln(''); buffer.writeln( ' if (response.statusCode == 200 || response.statusCode == 201) {'); buffer.writeln(' return jsonDecode(response.body);'); buffer.writeln(' } else {'); buffer.writeln( ' throw Exception(\'Failed to post data: \${response.statusCode}\');'); buffer.writeln(' }'); buffer.writeln(' }'); buffer.writeln('}'); buffer.writeln('```'); buffer.writeln(''); } /// 生成更新日志 void _generateChangeLog(StringBuffer buffer) { buffer.writeln('## 📝 更新日志'); buffer.writeln(''); buffer.writeln( '### ${document.version} - ${DateTime.now().toIso8601String().split('T')[0]}'); buffer.writeln(''); buffer.writeln('- 🎉 初始版本发布'); buffer.writeln('- 📡 ${document.paths.length} 个API端点'); buffer.writeln('- 📋 ${document.models.length} 个数据模型'); buffer.writeln('- 🔧 完整的API文档'); buffer.writeln(''); buffer.writeln('---'); buffer.writeln(''); buffer.writeln('*文档由 Swagger CLI By Max 自动生成*'); buffer.writeln(''); } /// 按控制器分组路径 Map> _groupPathsByController() { final groups = >{}; for (final path in document.paths.values) { final controllerName = StringUtils.extractControllerName(path); groups.putIfAbsent(controllerName, () => []).add(path); } return groups; } // 已移动到 StringUtils.extractControllerName /// 获取基础URL (从 OpenAPI 3.0 servers 配置) String _getBaseUrl() { if (document.servers.isNotEmpty) { return document.servers.first.url; } return '/'; // 默认相对路径 } /// 获取参数位置名称 String _getLocationName(ParameterLocation location) { switch (location) { case ParameterLocation.query: return '查询'; case ParameterLocation.path: return '路径'; case ParameterLocation.header: return '请求头'; case ParameterLocation.body: return '请求体'; case ParameterLocation.form: return '表单'; case ParameterLocation.cookie: return 'Cookie'; } } /// 获取属性类型描述 String _getPropertyTypeDescription(ApiProperty prop) { String baseType = prop.type.value; if (prop.format != null) { baseType += ' (${prop.format})'; } if (prop.nullable) { baseType += '?'; } return baseType; } /// 生成模型示例 String _generateModelExample(ApiModel model) { if (model.isEnum) { return '"${model.enumValues.first}"'; } final buffer = StringBuffer(); buffer.writeln('{'); final properties = model.properties.entries.toList(); for (int i = 0; i < properties.length; i++) { final entry = properties[i]; final propName = entry.key; final prop = entry.value; final exampleValue = _generatePropertyExample(prop); buffer.write(' "$propName": $exampleValue'); if (i < properties.length - 1) { buffer.write(','); } buffer.writeln(); } buffer.write('}'); return buffer.toString(); } /// 生成属性示例 String _generatePropertyExample(ApiProperty prop) { switch (prop.type) { case PropertyType.string: return '"string"'; case PropertyType.integer: return '0'; case PropertyType.number: return '0.0'; case PropertyType.boolean: return 'true'; case PropertyType.array: return '[]'; case PropertyType.object: return '{}'; case PropertyType.reference: return '{}'; default: return 'null'; } } /// 生成统计信息 Map _generateStats() { final stats = {}; stats['totalEndpoints'] = document.paths.length; stats['controllersCount'] = _groupPathsByController().length; stats['modelsCount'] = document.models.length; // HTTP方法统计 final methodStats = {}; for (final path in document.paths.values) { final method = path.method.value; methodStats[method] = (methodStats[method] ?? 0) + 1; } stats['methodStats'] = methodStats; return stats; } }