395 lines
13 KiB
Markdown
395 lines
13 KiB
Markdown
# XY Swagger Generator
|
||
|
||
基于 Swagger/OpenAPI 的 Dart/Flutter API/模型代码生成工具。
|
||
|
||
[](https://dart.dev/)
|
||
[](https://flutter.dev/)
|
||
[](https://spec.openapis.org/oas/v3.0.3/)
|
||
|
||
## ✨ 功能特性
|
||
|
||
### 🚀 核心功能
|
||
- **完整的 OpenAPI 3.0 支持**:涵盖所有主要规范特性
|
||
- **高性能解析**:支持并行解析和流式处理大型文档
|
||
- **智能代码生成**:生成高质量的 Dart/Flutter 代码
|
||
- **模块化架构**:按 API 模块自动分组生成
|
||
|
||
### 🎯 专为 Flutter 优化
|
||
- **Dio + Retrofit 集成**:完美适配主流网络架构
|
||
- **现代数据模型 (Freezed)**:生成基于 Freezed 的不可变数据模型,自动获得 `copyWith`、`toString`、`==/hashCode` 等功能。
|
||
- **类型安全**:生成强类型的 API 接口和模型
|
||
- **JSON 序列化**:与 `json_serializable` 无缝集成,自动生成序列化代码
|
||
- **文件上传支持**:完整的 multipart/form-data 支持
|
||
|
||
### 🔧 高级特性
|
||
- **智能缓存**:提升重复操作性能
|
||
- **错误诊断**:详细的错误报告和修复建议
|
||
- **性能监控**:内置性能统计和优化
|
||
- **增量生成**:支持增量更新和变更检测
|
||
- **枚举键名映射** ⭐NEW: 支持 `x-enum-varnames` 扩展字段和配置文件映射,生成有意义的枚举键名
|
||
|
||
## 🔍 当前状态要点
|
||
- 版本 **3.0.0**,命令入口统一为 `dart run swagger_generator_flutter generate`
|
||
- 支持从多个 `swagger_urls` 依次解析并按顺序合并,后者覆盖前者
|
||
- 生成的 API 会按版本落在 `api/<version>/`,类名自动添加 `V2`/`V3` 后缀(v1 保持不变)
|
||
- `included_tags` / `excluded_tags` 和 `ignored_directories` / `ignored_files` 可通过配置或 CLI 控制生成范围
|
||
- 模型按用途分类生成(request/result/enums/parameters),分页响应优先用统一的 `BasePageResult<T>`
|
||
- `generator_config.yaml` 放在哪里就按该目录解析相对路径,便于在宿主项目中以 dev dependency 使用
|
||
|
||
## 📚 **文档和规范**
|
||
|
||
### **核心文档**
|
||
- [**项目概览**](./docs/PROJECT_OVERVIEW.md) - 架构与功能总览
|
||
- [**使用指南**](./docs/USAGE_GUIDE.md) - 生成流程与最佳实践
|
||
- [**API 参考**](./docs/API_REFERENCE.md) - 核心类型与生成器说明
|
||
- [**快速参考**](./QUICK_REFERENCE.md) - 常见问题与命令速查
|
||
- [**配置模板**](./generator_config.template.yaml) - 复制为 `generator_config.yaml` 后按需调整
|
||
|
||
### **枚举键名映射** ⭐NEW
|
||
- [**快速参考**](./ENUM_QUICK_REFERENCE.md) - 枚举键名生成的三种方式对比
|
||
- [**使用指南**](./ENUM_KEY_NAMES_USAGE.md) - 详细的使用说明和后端实现示例
|
||
- [**实现总结**](./ENUM_CONFIG_MAPPING_SUMMARY.md) - 功能实现细节和测试验证
|
||
- [**配置示例**](./example/enum_config_mapping_example.yaml) - 完整的配置文件示例
|
||
|
||
### **设计原则**
|
||
1. **OpenAPI 3.0 标准优先** - 严格遵循规范,不进行主观推断
|
||
2. **与服务器保持一致** - swagger.json 是唯一真实来源
|
||
3. **有问题沟通文档** - 发现问题时要求完善后端文档
|
||
4. **类型安全第一** - 生成强类型代码,避免运行时错误
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 📦 作为 dev_dependencies 使用(推荐)
|
||
|
||
1) 在宿主项目 `pubspec.yaml` 添加依赖
|
||
```yaml
|
||
dependencies:
|
||
# Freezed 模型需要
|
||
freezed_annotation: ^2.4.1
|
||
json_annotation: ^4.8.1
|
||
|
||
dev_dependencies:
|
||
swagger_generator_flutter:
|
||
git:
|
||
url: https://github.com/your-org/swagger_generator_flutter.git
|
||
ref: main
|
||
# 或在开发阶段使用本地路径
|
||
# swagger_generator_flutter:
|
||
# path: ../swagger_generator_flutter
|
||
|
||
# 代码生成工具
|
||
build_runner: ^2.4.7
|
||
freezed: ^2.4.7
|
||
json_serializable: ^6.7.1
|
||
retrofit_generator: ^8.0.0 # 如果使用 Retrofit
|
||
```
|
||
|
||
2) 在宿主项目根目录准备 `generator_config.yaml`(复制 `generator_config.template.yaml`)
|
||
重点字段:
|
||
- `input.swagger_urls`:可配置多个 URL,后面的会覆盖前面的同名模型/路径(适合 v1 → v2 迭代)
|
||
- `output.base_dir/api_dir/models_dir`:输出目录,支持相对路径(基于配置文件所在目录)
|
||
- `output.included_tags / excluded_tags` 与 `ignored_directories / ignored_files`:控制生成范围和跳过文件
|
||
- `generation.api.version_extraction`:自定义版本提取正则与默认版本
|
||
- `generation.api.client`:设置 ApiClient 类名/文件名
|
||
- `generation.api.base_result_import / base_page_result_import`:接入自定义响应包装类型
|
||
|
||
3) 生成代码(两步)
|
||
```bash
|
||
# 步骤 1: 生成 Swagger API 和 Freezed 模型定义
|
||
flutter pub get
|
||
dart run swagger_generator_flutter generate --all
|
||
|
||
# 步骤 2: 运行 build_runner 生成 Freezed 和 json_serializable 的 part 文件
|
||
dart run build_runner build --delete-conflicting-outputs
|
||
```
|
||
CLI 里的 `--included-tags/--excluded-tags/--split-by-tags` 优先级高于配置文件。生成结果会按照版本落在 `api/<version>/` 下,模型分类在 `api_models/`。
|
||
|
||
4) 参考示例
|
||
`example/` 目录包含可直接运行的示例与 Makefile/generate_api.* 脚本,演示 dev dependency 场景。
|
||
|
||
### 💻 独立项目使用
|
||
1) 安装依赖
|
||
```bash
|
||
flutter pub get
|
||
```
|
||
2) 直接运行 CLI(可继续使用仓库内的 `generator_config.yaml` 配置)
|
||
```bash
|
||
dart run swagger_generator_flutter generate --all
|
||
# 或指定本地文件:在 swagger_urls 中写入 file:///absolute/path/to/swagger.json
|
||
```
|
||
3) 生成序列化文件
|
||
```bash
|
||
dart run build_runner build --delete-conflicting-outputs
|
||
```
|
||
|
||
### 🧩 编程式用法
|
||
```dart
|
||
import 'dart:io';
|
||
import 'package:swagger_generator_flutter/pipeline/parse/swagger_data_parser.dart';
|
||
import 'package:swagger_generator_flutter/pipeline/generate/impl/retrofit_api_generator.dart';
|
||
|
||
Future<void> main() async {
|
||
// 解析本地或远程的 Swagger 文档(支持 file:// 与 http(s)://)
|
||
final parser = SwaggerDataParser();
|
||
final document = await parser.fetchAndParseSwaggerDocument(
|
||
'file:///absolute/path/to/swagger.json',
|
||
);
|
||
|
||
// 生成 Dio + Retrofit 风格的 API
|
||
final generator = RetrofitApiGenerator(
|
||
className: 'ApiService',
|
||
useRetrofit: true,
|
||
useDio: true,
|
||
splitByTags: true,
|
||
versionedApi: true,
|
||
);
|
||
|
||
final code = generator.generateFromDocument(document);
|
||
await File('lib/api/api_service.dart').writeAsString(code);
|
||
}
|
||
```
|
||
|
||
## 📖 详细配置
|
||
|
||
### CLI 命令选项
|
||
|
||
#### 基本选项
|
||
- `--all` / `-a`: 生成所有文件(API + 模型 + 文档)
|
||
- `--api` / `-r`: 只生成 Retrofit 风格 API 接口
|
||
- `--models` / `-m`: 只生成数据模型
|
||
- `--docs` / `-d`: 只生成 API 文档
|
||
|
||
- `--split-by-tags` / `-t`: 按 tags 分组生成多个 API 文件(默认启用)
|
||
- `--output-dir` / `-o`: 指定输出目录(默认:generator)
|
||
|
||
#### 高级选项
|
||
- `--included-tags` / `-i`: 只生成指定 tags 的 API 和模型
|
||
- `--excluded-tags` / `-e`: 从生成中排除指定的 tags
|
||
|
||
**示例:**
|
||
```bash
|
||
# 只生成 User 和 Pet 相关的 API 和模型
|
||
dart run swagger_generator_flutter generate --all --included-tags=User,Pet
|
||
|
||
# 只生成 Store 相关的 API
|
||
dart run swagger_generator_flutter generate --api --included-tags=Store
|
||
|
||
# 生成多个指定 tags
|
||
dart run swagger_generator_flutter generate --all -i User,Pet,Order,Payment
|
||
```
|
||
|
||
**行为说明:**
|
||
- 如果某个 endpoint 有多个 tags,只要其中一个 tag 在 `included_tags` 列表中,就会生成该 endpoint
|
||
- 只生成被选中的 endpoints 所引用的 models,避免生成未使用的 model 代码
|
||
- 与 `split-by-tags` 选项完全兼容
|
||
|
||
### 配置文件选项
|
||
|
||
#### API Client 配置
|
||
|
||
可以在 `generator_config.yaml` 中自定义主 API 客户端的类名和文件名:
|
||
|
||
```yaml
|
||
generation:
|
||
api:
|
||
enabled: true
|
||
use_retrofit: true
|
||
use_dio: true
|
||
|
||
# API Client 配置
|
||
client:
|
||
class_name: "ApiClient" # API 客户端类名(默认: ApiClient)
|
||
file_name: "api_client" # API 客户端文件名(默认: api_client)
|
||
```
|
||
|
||
**使用场景:**
|
||
- **多项目/模块**:避免命名冲突,如 `ShopApiClient`、`UserApiClient`
|
||
- **项目规范**:符合团队命名规范,如 `AppApiService`、`NetworkClient`
|
||
- **模块化开发**:按模块划分,如 `PaymentModuleApi`、`OrderModuleApi`
|
||
|
||
**示例:**
|
||
```yaml
|
||
# 示例 1: 电商项目
|
||
client:
|
||
class_name: "ShopApiClient"
|
||
file_name: "shop_api_client"
|
||
|
||
# 示例 2: 用户模块
|
||
client:
|
||
class_name: "UserModuleApi"
|
||
file_name: "user_module_api"
|
||
|
||
# 示例 3: 应用级 API
|
||
client:
|
||
class_name: "AppApiService"
|
||
file_name: "app_api_service"
|
||
```
|
||
|
||
**生成结果:**
|
||
```dart
|
||
// 文件: lib/generated/api/shop_api_client.dart
|
||
class ShopApiClient {
|
||
final Dio _dio;
|
||
|
||
ShopApiClient(this._dio) {
|
||
_initApis();
|
||
}
|
||
|
||
// ... API 方法
|
||
}
|
||
```
|
||
|
||
📖 **更多示例**:查看 [API Client 配置示例](./examples/api_client_config_example.yaml)
|
||
|
||
### 生成器类型
|
||
|
||
#### 1. RetrofitApiGenerator(基础版)
|
||
```dart
|
||
final generator = RetrofitApiGenerator(
|
||
className: 'ApiService', // 生成的类名
|
||
splitByTags: true, // 是否按标签分割(默认启用)
|
||
useRetrofit: true, // 使用 Retrofit 注解
|
||
generateModels: true, // 生成模型类
|
||
);
|
||
```
|
||
|
||
|
||
|
||
### 解析器配置
|
||
```dart
|
||
final parser = PerformanceParser(
|
||
config: ParseConfig(
|
||
enableParallelParsing: true, // 并行解析
|
||
enableStreamParsing: false, // 流式解析
|
||
enableCaching: true, // 缓存
|
||
maxConcurrency: 4, // 最大并发数
|
||
enablePerformanceStats: true, // 性能统计
|
||
),
|
||
);
|
||
```
|
||
|
||
### 验证和错误处理
|
||
```dart
|
||
// 创建验证器
|
||
final validator = EnhancedValidator(
|
||
strictMode: false, // 严格模式
|
||
includeWarnings: true, // 包含警告
|
||
);
|
||
|
||
// 验证文档
|
||
final isValid = validator.validateDocument(document);
|
||
|
||
// 获取错误报告
|
||
final errorReport = validator.errorReporter.generateReport();
|
||
print(errorReport);
|
||
```
|
||
|
||
## 📊 性能优化
|
||
|
||
### 缓存策略
|
||
当前版本不再内置 SmartCache。建议在业务层按需实现缓存(如内存 Map + 过期时间)或使用第三方库。示例:
|
||
```dart
|
||
class SimpleCache {
|
||
final _store = <String, (Object value, DateTime expireAt)>{};
|
||
|
||
T? get<T>(String key) {
|
||
final entry = _store[key];
|
||
if (entry == null) return null;
|
||
if (DateTime.now().isAfter(entry.$2)) {
|
||
_store.remove(key);
|
||
return null;
|
||
}
|
||
return entry.$1 as T;
|
||
}
|
||
|
||
void put(String key, Object value, {Duration ttl = const Duration(minutes: 30)}) {
|
||
_store[key] = (value, DateTime.now().add(ttl));
|
||
}
|
||
}
|
||
```
|
||
|
||
### 性能监控
|
||
```dart
|
||
// 获取解析性能统计
|
||
final parseStats = parser.lastStats;
|
||
print('解析性能统计:');
|
||
print(' 总时间: ${parseStats?.totalTime.inMilliseconds}ms');
|
||
print(' 路径数: ${parseStats?.pathCount}');
|
||
print(' 吞吐量: ${parseStats?.bytesPerSecond.toStringAsFixed(2)} bytes/s');
|
||
```
|
||
|
||
## 📁 目录结构
|
||
```
|
||
swagger_generator_flutter/
|
||
bin/ # CLI 入口(main & swagger_generator_flutter)
|
||
docs/ # 项目文档(概览、使用指南、API 参考)
|
||
example/ # dev dependency 场景示例(含 make / 脚本)
|
||
generator/ # 默认输出目录示例(生成的文档)
|
||
lib/ # 核心代码
|
||
commands/ # CLI 命令(GenerateCommand 等)
|
||
core/ # 配置、模型、异常
|
||
pipeline/ # 核心处理流程 (Parse -> Validate -> Generate -> Render -> Output)
|
||
utils/ # FileUtils、StringUtils 等
|
||
tests/ # 基础测试示例
|
||
generator_config.template.yaml
|
||
```
|
||
|
||
## 运行测试
|
||
```bash
|
||
dart run test tests/
|
||
```
|
||
|
||
## 🎯 **规范遵循示例**
|
||
|
||
### **✅ 正确的生成结果**
|
||
```dart
|
||
// 严格按照 swagger.json 定义生成
|
||
@POST('/api/v1/Login/userLogin')
|
||
Future<BaseResult<UserLoginResult>> userLogin(
|
||
@Body() LoginRequest request // 仅当 swagger 中明确定义时
|
||
);
|
||
|
||
// 健康检查接口
|
||
@GET('/health')
|
||
Future<BaseResult<void>> healthCheck();
|
||
|
||
// 无明确 schema 的接口
|
||
@POST('/api/v1/Action/DoSomething')
|
||
Future<BaseResult<Map<String, dynamic>>> doSomething();
|
||
```
|
||
|
||
### **❌ 避免的错误做法**
|
||
```dart
|
||
// 错误:生成不存在的类型
|
||
Future<BaseResult<TaskInfoResult>> someMethod();
|
||
|
||
// 错误:添加 swagger 中未定义的参数
|
||
@POST('/api/v1/GetUsers')
|
||
Future<BaseResult<List<User>>> getUsers(
|
||
@Body() Map<String, dynamic> request // swagger 中没有定义
|
||
);
|
||
|
||
// 错误:基于关键词推断类型
|
||
if (path.contains('login')) return 'UserLoginResult';
|
||
```
|
||
|
||
## 贡献指南
|
||
- 在提交前请先跑通 `dart run swagger_generator_flutter generate --all` 与必要的 `build_runner`
|
||
- 参考 [docs/USAGE_GUIDE.md](./docs/USAGE_GUIDE.md) 和 [QUICK_REFERENCE.md](./QUICK_REFERENCE.md) 保持生成规则一致
|
||
- 新增能力需补充最小可复现示例或测试
|
||
- 生成规则/命名风格如有特殊需求请在 issue 说明并同步更新文档
|
||
|
||
## 常见问题
|
||
- **生成的类型不存在?** 检查 swagger.json 中是否定义了对应的 schema
|
||
- **接口缺少参数?** 确认 swagger.json 中是否有完整的参数定义
|
||
- **可空性不正确?** 检查 swagger.json 中的 nullable 字段设置
|
||
- 更多问题请参考 [快速参考指南](./QUICK_REFERENCE.md)
|
||
### 命令行提示
|
||
- 查看所有选项:`dart run swagger_generator_flutter generate --help`
|
||
- 旧版 `run_swagger.sh/.bat` 已移除,统一使用 `dart run` 入口
|
||
|
||
---
|
||
|
||
更新日期:2025-11-09
|
||
作者:Max
|