Compare commits

..

No commits in common. "b47a823db417e6d63ae33d8b90f9f37c81a35875" and "2e7ae3fc70d3274e515a588adf643d0c60672cac" have entirely different histories.

46 changed files with 14456 additions and 7161 deletions

View File

@ -1,87 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
## [2.1.1] - 2025-11-05
### 🎉 新特性
#### 作为 dev_dependencies 支持
- ✅ 添加 `executables` 配置,支持作为 dev_dependencies 在其他项目中使用
- ✅ 可通过 `dart run swagger_generator_flutter generate` 命令执行
- ✅ 支持从使用者项目根目录读取 `generator_config.yaml` 配置文件
- ✅ 新增完整的使用指南 `USAGE_AS_DEV_DEPENDENCY.md`
- ✅ 提供配置文件模板 `generator_config.template.yaml`
### 📝 文档更新
- 更新 README.md新增 "作为 dev_dependencies 使用" 章节
- 新增详细的集成指南,包含 CI/CD 示例
- 提供完整的工作流程说明
### 🔧 配置优化
- 更新 `pubspec.yaml` 描述信息
- 添加可执行命令映射配置
---
## [2.1.0] - 2025-11-03
### 🎉 主要新特性
#### 多版本 API 支持
- 支持解析多个 Swagger 文档 URLv1/v2/v3...
- API 文件按版本自动分目录api/v1/、api/v2/
- V1 版本类名保持简洁(不加后缀,如 `MobileManagerApi`
- V2+ 版本类名添加版本号后缀(如 `MobileManagerApiV2`
- ApiClient 提供清晰的版本化访问器
#### 模型智能分类
- 新增 `ModelUsageType` 枚举request/response/common/unknown
- 自动分析 Swagger paths 判定模型实际用途
- 精确区分请求参数和返回结果
#### 目录结构优化
- `api_models/enums/` - 枚举类型
- `api_models/request/` - 请求模型
- `api_models/result/` - 响应模型
- `api_models/parameters/` - 查询参数类
- 每个子目录自动生成 `index.dart` 便于导入
### ✨ 改进
#### 默认值处理
- 响应模型的 List 类型自动添加 `@JsonKey(defaultValue: [])`
- 请求模型不添加默认值,避免影响后端接收
- 更安全的 null 值处理
#### 导入策略
- API 文件统一使用 `import '../../api_models/index.dart'`
- 利用 Dart tree-shaking不影响应用大小
- 简化导入管理,提高可维护性
### 🔧 配置变更
- `SwaggerConfig.swaggerJsonUrls` 现在支持多个 Swagger 文档 URL
- 移除了单独的 `swaggerJsonUrl` 配置项
### 📝 文档
- 新增 `CANCELTOKEN_USAGE_GUIDE.md` - CancelToken 使用指南
- 新增 `CHANGELOG_CANCELTOKEN.md` - CancelToken 功能变更日志
---
## [2.0.1] - 2025-10-XX
### 改进
- 性能优化和 bug 修复
- 代码质量提升
---
## [2.0.0] - 2025-09-XX
### 重大变更
- 项目重构
- 支持 Retrofit
- 优化代码生成逻辑

231
CODE_REVIEW_CHECKLIST.md Normal file
View File

@ -0,0 +1,231 @@
# Augment 代码生成审查清单
## 📋 **生成前检查**
### **Swagger 文档验证**
- [ ] swagger.json 文件格式正确
- [ ] OpenAPI 版本为 3.0.x
- [ ] 所有 $ref 引用都存在对应的定义
- [ ] components/schemas 部分完整
- [ ] 所有接口都有明确的 responses 定义
- [ ] 参数定义完整name, in, schema
- [ ] requestBody 定义明确(如果需要)
### **环境准备**
- [ ] Dart SDK 版本 >= 3.0.0
- [ ] 依赖包版本正确
- [ ] 网络连接正常(如果从 URL 获取 swagger
- [ ] 输出目录权限正确
---
## 🔧 **生成过程检查**
### **命令执行**
- [ ] 使用正确的生成命令
- [ ] 参数配置正确
- [ ] 没有错误或警告信息
- [ ] 生成统计信息合理
### **文件生成**
- [ ] API 文件按 tag 正确分组
- [ ] 模型文件命名规范
- [ ] 目录结构正确
- [ ] index.dart 文件更新
---
## ✅ **生成后验证**
### **API 接口检查**
#### **文件结构**
- [ ] 文件头注释完整
- [ ] 导入语句正确且按需导入
- [ ] 类名符合 PascalCase 规范
- [ ] 方法名符合 camelCase 规范
#### **方法定义**
- [ ] HTTP 方法注解正确(@GET, @POST, @PUT, @DELETE
- [ ] 路径定义与 swagger 一致
- [ ] 返回类型正确提取
- [ ] 参数定义完整且正确
```dart
// ✅ 正确的方法定义示例
/// 用户登录
@POST('/api/v1/Login/userLogin')
Future<BaseResult<UserLoginResult>> userLogin(
@Body() LoginRequest request
);
// ❌ 错误示例
@POST('/api/v1/Login/userLogin')
Future<BaseResult<TaskInfoResult>> userLogin( // 错误:不存在的类型
@Body() Map<String, dynamic> request // 错误swagger 中没有定义
);
```
#### **参数检查**
- [ ] 路径参数使用 @Path 注解
- [ ] 查询参数使用 @Query 注解
- [ ] 请求体参数使用 @Body 注解
- [ ] 参数类型与 swagger 定义一致
- [ ] 可空性正确(? 标记)
- [ ] 没有多余的参数
#### **返回类型检查**
- [ ] 使用 BaseResult 包装
- [ ] 分页接口使用 BasePageResult
- [ ] 健康检查接口返回 void
- [ ] 泛型类型存在于 swagger 中
- [ ] 没有硬编码推断的类型
#### **导入检查**
- [ ] 基础类型导入正确
- [ ] 分页类型按需导入
- [ ] 模型类型导入完整
- [ ] 没有未使用的导入
- [ ] 导入路径正确
### **数据模型检查**
#### **类定义**
- [ ] 类名符合 PascalCase 规范
- [ ] @JsonSerializable 注解正确
- [ ] 注解参数配置正确checked: true, includeIfNull: false
#### **属性定义**
- [ ] 属性名符合 camelCase 规范
- [ ] 类型映射正确string -> String, integer -> int
- [ ] 可空性严格按照 nullable 字段
- [ ] 注释信息完整
- [ ] final 修饰符正确使用
```dart
// ✅ 正确的属性定义
/// 用户ID
final int id;
/// 用户名
final String username;
/// 昵称(可空)
final String? nickname; // swagger 中 "nullable": true
// ❌ 错误示例
final String? username; // 错误swagger 中没有 "nullable": true
final String nickname; // 错误swagger 中有 "nullable": true
```
#### **构造函数检查**
- [ ] 使用 const 构造函数
- [ ] 非空字段标记为 required
- [ ] 可空字段不使用 required
- [ ] 参数顺序合理
#### **序列化方法**
- [ ] fromJson 工厂方法存在
- [ ] toJson 方法存在
- [ ] part 文件引用正确
- [ ] 生成的方法名正确
### **代码质量检查**
#### **静态分析**
- [ ] dart analyze 无错误
- [ ] dart analyze 无警告
- [ ] 代码格式化正确
- [ ] 命名规范一致
#### **类型安全**
- [ ] 避免使用 dynamic
- [ ] 泛型类型明确
- [ ] 可空性处理正确
- [ ] 类型转换安全
#### **性能考虑**
- [ ] 使用 const 构造函数
- [ ] 避免不必要的对象创建
- [ ] 导入优化
- [ ] 内存使用合理
---
## 🚫 **常见问题检查**
### **类型相关**
- [ ] 没有生成不存在的类型(如 TaskInfoResult
- [ ] 没有硬编码的类型映射
- [ ] 没有基于路径关键词的推断
- [ ] 没有基于 tag 的类型推断
### **参数相关**
- [ ] 没有添加 swagger 中未定义的参数
- [ ] 没有遗漏 swagger 中定义的参数
- [ ] 参数位置正确path, query, body
- [ ] 参数类型正确
### **导入相关**
- [ ] 没有循环导入
- [ ] 没有未使用的导入
- [ ] 没有缺失的导入
- [ ] 导入路径正确
### **命名相关**
- [ ] 文件名符合 snake_case
- [ ] 类名符合 PascalCase
- [ ] 方法名符合 camelCase
- [ ] 变量名符合 camelCase
---
## 🔄 **集成测试**
### **构建测试**
- [ ] dart pub get 成功
- [ ] dart pub run build_runner build 成功
- [ ] 生成的 .g.dart 文件正确
- [ ] 没有构建错误或警告
### **运行时测试**
- [ ] API 调用正常
- [ ] 序列化/反序列化正常
- [ ] 类型转换正确
- [ ] 错误处理正确
### **兼容性测试**
- [ ] Dart 版本兼容
- [ ] Flutter 版本兼容
- [ ] 依赖包版本兼容
- [ ] 平台兼容性
---
## 📝 **文档检查**
### **代码注释**
- [ ] 类注释完整
- [ ] 方法注释完整
- [ ] 属性注释完整
- [ ] 特殊逻辑有说明
### **生成信息**
- [ ] 文件头信息正确
- [ ] 版本信息正确
- [ ] 生成时间标记
- [ ] 来源信息明确
---
## ✍️ **审查签名**
**审查者**: _______________
**审查日期**: _______________
**审查结果**: [ ] 通过 [ ] 需要修改 [ ] 拒绝
**备注**: _______________
---
**检查清单版本**: v2.0
**最后更新**: 2025-01-24

View File

@ -1,242 +0,0 @@
# 代码审核报告 - 版本变动审核
**审核日期**: 2025-11-05
**审核范围**: 本次版本的所有变更
**审核重点**: 文件头配置功能、文件跳过功能
---
## 📋 本次版本主要变更
### 1. 文件头配置功能 ✅
- **新增**: `ConfigLoader.getFileHeaderTemplate()` - 读取文件头模板
- **新增**: `ConfigLoader.getGeneratorName()` - 读取生成器名称
- **新增**: `ConfigLoader.getAuthor()` - 读取作者信息
- **新增**: `ConfigLoader.getCopyright()` - 读取版权信息
- **更新**: `StringUtils.generateFileHeader()` - 支持配置模板和变量替换
- **更新**: `BaseGenerator.generateFileHeader()` - 传递文件名参数
### 2. 文件跳过功能 ✅
- **新增**: `ConfigLoader.getIgnoredDirectories()` - 读取跳过的目录列表
- **新增**: `ConfigLoader.getIgnoredFiles()` - 读取跳过的文件名列表
- **新增**: `ConfigLoader.shouldSkipFile()` - 检查文件是否应该跳过
- **更新**: `GenerateCommand.execute()` - 在所有文件生成点添加跳过检查
### 3. 配置文件更新 ✅
- **更新**: `generator_config.yaml` - 添加模板配置示例
- **更新**: `generator_config.template.yaml` - 添加模板配置部分
- **更新**: `example/as_dev_dependency/generator_config.yaml` - 添加完整模板配置
---
## ✅ 代码质量检查
### 1. 代码逻辑检查
#### ✅ 文件头配置逻辑
- **状态**: ✅ 正确
- **说明**:
- 模板变量替换逻辑正确
- 支持默认值回退机制
- 当配置不存在时使用默认模板
#### ✅ 文件跳过逻辑
- **状态**: ✅ 正确
- **说明**:
- 目录级别跳过:路径标准化处理正确
- 文件名级别跳过:支持精确匹配和通配符匹配
- 边界情况处理:空目录、空文件名都有处理
#### ✅ Swagger URL 处理
- **状态**: ✅ 正确
- **说明**:
- 支持多个 Swagger URL
- 文件头使用第一个 URL合理
- URL 合并逻辑正确
### 2. 代码完整性检查
#### ✅ 所有文件生成点都已添加跳过检查
- 模型文件生成 ✅
- API 文件生成 ✅
- 版本目录生成 ✅
- 主 API 文件生成 ✅
- 参数实体类生成 ✅
- 文档文件生成 ✅
#### ✅ 配置文件完整性
- 模板配置项完整 ✅
- 注释说明完整 ✅
- 示例配置正确 ✅
---
## ⚠️ 发现的问题
### 1. 未使用的方法
**位置**: `lib/generators/retrofit_api_generator.dart:1037`
**问题**: `_getRequiredModelImports()` 方法未被引用
**影响**: 低(不影响功能)
**建议**:
```dart
// 可以考虑删除或标记为 @deprecated
// 或者保留以备将来使用
```
**处理**: 暂不处理,保留以备将来使用
---
## 🔍 潜在问题分析
### 1. 文件头模板变量替换
**潜在问题**: 当模板包含多个相同的变量时,`replaceAll` 会替换所有出现
**影响**: 低(通常模板中每个变量只出现一次)
**验证**: ✅ 代码逻辑正确,`replaceAll` 是正确的选择
### 2. 文件跳过路径匹配
**潜在问题**: 路径匹配可能在某些边界情况下不够精确
**影响**: 低(已处理主要边界情况)
**验证**: ✅ 代码包含边界检查:
- 空目录名检查
- 路径标准化(统一使用 `/`
- 目录边界检查
### 3. 多版本文件头 URL
**潜在问题**: 当有多个 Swagger URL 时,文件头只显示第一个 URL
**影响**: 低(这是合理的设计选择)
**说明**: 这是有意为之的设计,因为:
- 文件头通常只需要显示一个来源
- 多个 URL 可能导致文件头过长
- 如果需要,可以通过配置模板自定义
---
## 📊 代码质量指标
### 代码覆盖率
- ✅ 所有新增功能都有对应的配置选项
- ✅ 所有配置都有默认值处理
- ✅ 所有边界情况都有处理
### 错误处理
- ✅ 配置文件不存在时使用默认值
- ✅ 配置解析失败时显示警告
- ✅ 文件跳过检查失败时继续执行(不影响主流程)
### 代码一致性
- ✅ 命名规范一致
- ✅ 代码风格一致
- ✅ 注释风格一致
---
## 🎯 功能验证
### 1. 文件头配置功能 ✅
**测试场景**:
- ✅ 配置存在时使用配置的模板
- ✅ 配置不存在时使用默认模板
- ✅ 模板变量正确替换
- ✅ 生成器信息从配置读取
**验证结果**: ✅ 所有场景通过
### 2. 文件跳过功能 ✅
**测试场景**:
- ✅ 目录级别跳过
- ✅ 文件名级别跳过(精确匹配)
- ✅ 文件名级别跳过(通配符匹配)
- ✅ 组合跳过(目录 + 文件名)
**验证结果**: ✅ 所有场景通过
---
## 📝 建议改进
### 1. 文档完善 ✅
- ✅ 已添加 `FILE_HEADER_CONFIGURATION.md`
- ✅ 配置文件包含详细注释
- ✅ 示例配置完整
### 2. 代码优化建议
#### 建议 1: 考虑添加单元测试
- **优先级**: 中
- **说明**: 为新功能添加单元测试可以提高代码质量
#### 建议 2: 优化文件跳过性能
- **优先级**: 低
- **说明**: 当前实现已经足够高效,但如果文件数量很大,可以考虑缓存配置
### 3. 功能增强建议
#### 建议 1: 支持更多通配符模式
- **优先级**: 低
- **说明**: 当前支持 `*prefix`, `suffix*`, `*pattern*`,已满足大部分需求
#### 建议 2: 支持正则表达式匹配
- **优先级**: 低
- **说明**: 如果需要更复杂的匹配模式,可以考虑支持正则表达式
---
## ✅ 总结
### 代码质量
- ✅ **优秀**: 代码结构清晰,逻辑正确
- ✅ **完整**: 所有功能都有完整的实现
- ✅ **健壮**: 错误处理和边界情况处理完善
### 功能完整性
- ✅ **文件头配置**: 完全实现,功能完整
- ✅ **文件跳过**: 完全实现,功能完整
- ✅ **配置支持**: 配置项完整,注释详细
### 潜在风险
- ⚠️ **低风险**: 只有一个未使用的方法,不影响功能
- ✅ **无高风险问题**: 未发现高风险问题
### 建议
1. ✅ **可以发布**: 代码质量良好,可以发布
2. ✅ **文档完善**: 文档已完善,用户可以理解如何使用
3. ✅ **向后兼容**: 新功能不影响现有功能,向后兼容
---
## 📋 审核结论
**审核状态**: ✅ **通过**
**总体评价**:
- 代码质量优秀
- 功能实现完整
- 错误处理完善
- 文档齐全
**建议**: 可以发布此版本
**备注**:
- 发现一个未使用的方法(`_getRequiredModelImports`),但不影响功能,可以保留以备将来使用
- 所有核心功能都已正确实现并通过验证
---
**审核人**: AI Assistant
**审核日期**: 2025-11-05

View File

@ -1,293 +0,0 @@
# 📦 Dev Dependency 功能完成总结
本文档总结了将 `swagger_generator_flutter` 配置为 dev_dependencies 的所有改动。
## ✅ 完成的工作
### 1. 核心配置文件修改
#### `pubspec.yaml`
- ✅ 添加 `executables` 配置
- ✅ 映射命令:`swagger_generator: main`
- ✅ 更新描述信息
- ✅ 版本升级到 2.1.1
```yaml
executables:
swagger_generator: main
```
这使得其他项目可以通过以下命令使用:
```bash
dart run swagger_generator_flutter generate --all
```
### 2. 新增文档文件
#### `USAGE_AS_DEV_DEPENDENCY.md`
完整的使用指南,包含:
- 📦 安装步骤
- 📝 配置文件说明
- 🚀 使用方法3种方式
- 📋 命令选项说明
- 📂 生成的文件结构
- 🔧 必需的项目依赖
- 🔄 完整工作流程
- 🎯 CI/CD 集成示例
- 🐛 常见问题排除
- 💡 最佳实践
#### `generator_config.template.yaml`
配置文件模板,用户可以:
- 复制到自己的项目
- 根据项目需求修改
- 包含详细的配置说明和注释
### 3. 示例项目(`example/as_dev_dependency/`
创建了完整的示例应用,包含以下文件:
#### 项目配置文件
- ✅ `pubspec.yaml` - 依赖配置(使用本地路径引用)
- ✅ `generator_config.yaml` - 生成器配置(使用 Petstore API
- ✅ `analysis_options.yaml` - 代码分析配置
- ✅ `.gitignore` - Git 忽略配置
#### 基础代码文件
- ✅ `lib/common/api_response.dart` - 通用 API 响应包装类
- ✅ `lib/common/paged_response.dart` - 分页响应包装类
- ✅ `lib/main.dart` - Flutter 应用入口(带使用说明)
#### 自动化脚本
- ✅ `generate_api.sh` - macOS/Linux 生成脚本
- ✅ `generate_api.bat` - Windows 生成脚本
- ✅ `Makefile` - Make 命令配置12+ 命令)
#### 文档
- ✅ `README.md` - 完整的项目说明
- ✅ `QUICK_START.md` - 5分钟快速开始指南
### 4. 主项目文档更新
#### `README.md`
- ✅ 新增 "📦 作为 dev_dependencies 使用" 章节
- ✅ 提供快速开始步骤
- ✅ 添加文档链接
#### `CHANGELOG.md`
- ✅ 新增 [2.1.1] 版本说明
- ✅ 详细记录所有新特性
- ✅ 说明文档更新
## 📂 文件结构总览
```
swagger_generator_flutter/
├── pubspec.yaml ← 更新:添加 executables
├── CHANGELOG.md ← 更新:新增 2.1.1 版本
├── README.md ← 更新:新增使用章节
├── USAGE_AS_DEV_DEPENDENCY.md ← 新增:完整使用指南
├── generator_config.template.yaml ← 新增:配置模板
├── DEV_DEPENDENCY_SETUP_SUMMARY.md ← 新增:本文件
└── example/
└── as_dev_dependency/ ← 新增:完整示例项目
├── pubspec.yaml
├── generator_config.yaml
├── analysis_options.yaml
├── .gitignore
├── Makefile
├── generate_api.sh ← 可执行
├── generate_api.bat
├── README.md
├── QUICK_START.md
└── lib/
├── common/
│ ├── api_response.dart
│ └── paged_response.dart
└── main.dart
```
## 🎯 使用方式总结
### 在其他项目中使用
#### 1. 添加依赖
```yaml
dev_dependencies:
swagger_generator_flutter:
git:
url: https://github.com/your-org/swagger_generator_flutter.git
ref: develop
```
#### 2. 创建配置
```bash
cp node_modules/swagger_generator_flutter/generator_config.template.yaml \
generator_config.yaml
```
#### 3. 生成代码
```bash
dart run swagger_generator_flutter generate --all
dart run build_runner build --delete-conflicting-outputs
```
### 测试示例项目
```bash
cd example/as_dev_dependency
flutter pub get
./generate_api.sh # 或 make build
flutter run
```
## 📋 功能清单
### ✅ 已实现功能
- [x] 作为 dev_dependencies 使用
- [x] 通过 `dart run` 命令执行
- [x] 从项目根目录读取配置文件
- [x] 完整的使用文档
- [x] 配置文件模板
- [x] 完整的示例项目
- [x] 自动化生成脚本Shell/Batch
- [x] Makefile 命令支持
- [x] CI/CD 集成示例
- [x] 故障排除指南
- [x] 最佳实践建议
### 🎓 文档完整性
- [x] 快速开始指南QUICK_START.md
- [x] 完整使用指南USAGE_AS_DEV_DEPENDENCY.md
- [x] 示例项目文档example/as_dev_dependency/README.md
- [x] 配置文件模板generator_config.template.yaml
- [x] 主 README 更新
- [x] CHANGELOG 更新
### 🛠️ 辅助工具
- [x] Shell 脚本macOS/Linux
- [x] Batch 脚本Windows
- [x] Makefile12+ 命令)
- [x] .gitignore 配置
- [x] analysis_options.yaml
## 🚀 下一步建议
### 发布前检查
1. **测试示例项目**
```bash
cd example/as_dev_dependency
make build
flutter run
```
2. **验证配置文件**
- 确保 `generator_config.template.yaml` 包含所有必要配置
- 验证路径和导入是否正确
3. **文档审查**
- 检查所有链接是否有效
- 确保示例代码可以运行
- 验证命令是否正确
4. **版本发布**
- 更新版本号到 2.1.1
- 创建 Git tag
- 发布到仓库
### 可选增强功能
- [ ] 添加更多示例(不同的 Swagger API
- [ ] 创建视频教程
- [ ] 添加单元测试
- [ ] 创建 GitHub Actions 工作流模板
- [ ] 添加性能基准测试
- [ ] 创建 VSCode 插件/扩展
## 💡 使用技巧
### 1. 本地开发时使用相对路径
```yaml
dev_dependencies:
swagger_generator_flutter:
path: ../swagger_generator_flutter
```
### 2. 生产环境使用 Git 引用
```yaml
dev_dependencies:
swagger_generator_flutter:
git:
url: https://github.com/your-org/swagger_generator_flutter.git
ref: v2.1.1 # 使用具体版本标签
```
### 3. 使用 Makefile 简化命令
```bash
make build # 代替长命令
make watch # 监听模式
make clean # 清理
```
### 4. CI/CD 集成
```yaml
# .github/workflows/build.yml
- name: Generate API
run: dart run swagger_generator_flutter generate --all
```
## 📊 对比:改动前后
### 改动前
- ❌ 只能作为独立项目使用
- ❌ 需要复制整个项目到工作区
- ❌ 配置不灵活
- ❌ 缺少使用文档
### 改动后
- ✅ 可以作为 dev_dependencies 集成
- ✅ 通过包管理器安装
- ✅ 灵活的配置系统
- ✅ 完整的文档和示例
- ✅ 自动化工具支持
- ✅ CI/CD 友好
## 🎉 总结
现在 `swagger_generator_flutter` 已经完全支持作为 dev_dependencies 使用!
**主要优势:**
1. 🚀 **易于集成** - 一行依赖配置即可使用
2. 📝 **配置灵活** - 通过 YAML 文件自定义所有选项
3. 🔄 **工作流友好** - 提供脚本和 Makefile 支持
4. 📚 **文档完善** - 从快速开始到高级用法都有详细说明
5. 🎯 **实战示例** - 完整的示例项目可以直接运行
**用户体验:**
- 从添加依赖到生成代码只需 3 步
- 5 分钟即可开始使用
- 完善的错误处理和故障排除指南
- 支持多种使用方式命令行、脚本、Makefile
## 📞 支持
如有问题,请参考:
1. [快速开始指南](example/as_dev_dependency/QUICK_START.md)
2. [完整使用指南](USAGE_AS_DEV_DEPENDENCY.md)
3. [示例项目](example/as_dev_dependency/)
4. [主文档](README.md)
---
**版本**: 2.1.1
**更新日期**: 2025-11-05
**状态**: ✅ 完成

View File

@ -1,330 +0,0 @@
# ✅ 文件头注释配置功能
## 📋 功能说明
已添加文件头注释的配置支持,可以通过 `generator_config.yaml` 自定义生成的文件头格式。
**实现日期**: 2025-11-05
**状态**: ✅ 已完成
---
## 🎯 功能特性
### 1. 配置文件头模板
`generator_config.yaml` 中的 `templates.file_header` 配置项可以自定义文件头格式:
```yaml
# 模板配置
templates:
# 文件头模板
file_header: |
// {fileType}
// 基于 Swagger API 文档: {swaggerUrl}
// 由 {generatorName} by {author} 生成
// {copyright}
```
### 2. 支持的模板变量
| 变量 | 说明 | 示例 |
|------|------|------|
| `{fileName}` | 文件名(完整文件名) | `user_api.dart` |
| `{fileType}` | 文件类型描述 | `API 接口定义`、`模型定义` |
| `{swaggerUrl}` | Swagger 文档 URL | `https://api.example.com/swagger.json` |
| `{generatorName}` | 生成器名称 | `xy_swagger_generator` |
| `{author}` | 作者信息 | `max` |
| `{copyright}` | 版权信息 | `Copyright (C) 2025 YuanXuan. All rights reserved.` |
### 3. 生成器信息配置
生成器信息可以从 `generator` 配置项中读取:
```yaml
generator:
name: "my_custom_generator" # 生成器名称
version: "1.0" # 版本号
author: "Your Name" # 作者
copyright: "Copyright (C) 2025 Your Company. All rights reserved." # 版权信息
```
---
## 📝 配置示例
### 示例 1: 默认模板(不配置时使用)
如果不配置 `templates.file_header`,会使用默认模板:
```dart
// HealthCheck API 接口定义
// 基于 Swagger API 文档: https://api.example.com/swagger.json
// 由 xy_swagger_generator by max 生成
// Copyright (C) 2025 YuanXuan. All rights reserved.
```
### 示例 2: 自定义模板(包含文件名)
```yaml
templates:
file_header: |
// {fileName} - {fileType}
// 基于 Swagger API 文档: {swaggerUrl}
// 由 {generatorName} by {author} 生成
// {copyright}
```
**生成结果**:
```dart
// health_check_api.dart - HealthCheck API 接口定义
// 基于 Swagger API 文档: https://api.example.com/swagger.json
// 由 xy_swagger_generator by max 生成
// Copyright (C) 2025 YuanXuan. All rights reserved.
```
### 示例 3: 简洁模板
```yaml
templates:
file_header: |
// {fileType}
// Generated by {generatorName}
// {copyright}
```
**生成结果**:
```dart
// HealthCheck API 接口定义
// Generated by xy_swagger_generator
// Copyright (C) 2025 YuanXuan. All rights reserved.
```
### 示例 4: 多行模板(添加空行)
```yaml
templates:
file_header: |
// {fileType}
//
// 基于 Swagger API 文档: {swaggerUrl}
// 由 {generatorName} by {author} 生成
// {copyright}
```
**生成结果**:
```dart
// HealthCheck API 接口定义
//
// 基于 Swagger API 文档: https://api.example.com/swagger.json
// 由 xy_swagger_generator by max 生成
// Copyright (C) 2025 YuanXuan. All rights reserved.
```
### 示例 5: 公司规范模板
```yaml
generator:
name: "company_api_generator"
author: "Dev Team"
copyright: "Copyright (C) 2025 Company Inc. All rights reserved."
templates:
file_header: |
/**
* {fileType}
*
* @file {fileName}
* @generated {generatorName} by {author}
* @source {swaggerUrl}
* @copyright {copyright}
*/
```
**生成结果**:
```dart
/**
* HealthCheck API 接口定义
*
* @file health_check_api.dart
* @generated company_api_generator by Dev Team
* @source https://api.example.com/swagger.json
* @copyright Copyright (C) 2025 Company Inc. All rights reserved.
*/
```
---
## 🔍 实现细节
### 1. 配置读取流程
1. **读取模板**: 从 `templates.file_header` 读取模板字符串
2. **读取生成器信息**: 从 `generator` 配置项读取 `name`、`author`、`copyright`
3. **变量替换**: 使用实际值替换模板中的变量
4. **生成文件头**: 将替换后的模板作为文件头
### 2. 默认值处理
如果配置项不存在,使用默认值:
- `generator.name`: `'xy_swagger_generator'`
- `generator.author`: `'max'`
- `generator.copyright`: `'Copyright (C) 2025 YuanXuan. All rights reserved.'`
- `templates.file_header`: 使用默认模板格式
### 3. 文件类型说明
不同文件类型会生成不同的描述:
- **API 接口文件**: `{tagName} API 接口定义`
- **模型文件**: `{modelName} 模型定义`
- **参数实体类**: `参数实体类 - {className}`
- **索引文件**: `API 模型导出文件`
---
## ✅ 测试验证
### 测试场景
1. ✅ **默认模板** - 不配置时使用默认模板
2. ✅ **自定义模板** - 配置模板后使用自定义格式
3. ✅ **变量替换** - 所有模板变量正确替换
4. ✅ **生成器信息** - 从配置读取生成器信息
5. ✅ **多种文件类型** - API、模型、参数实体类都使用配置的模板
### 测试命令
```bash
cd example/as_dev_dependency
# 1. 配置自定义模板
# 在 generator_config.yaml 中添加:
# templates:
# file_header: |
# // {fileType}
# // Generated by {generatorName}
# 2. 运行生成
dart run swagger_generator_flutter generate --all
# 3. 检查生成的文件头
head -5 generator/api/v2/follow_manager_api.dart
```
---
## 📊 输出示例
### 使用默认模板
```dart
// HealthCheck API 接口定义
// 基于 Swagger API 文档: https://quanxue-test-api.w.23544.com:8843/swagger/v1/swagger.json
// 由 xy_swagger_generator by max 生成
// Copyright (C) 2025 YuanXuan. All rights reserved.
```
### 使用自定义模板
```dart
// HealthCheck API 接口定义
// 基于 Swagger API 文档: https://quanxue-test-api.w.23544.com:8843/swagger/v1/swagger.json
// 由 example_app_generator by Example Team 生成
// Copyright (C) 2025 Example Company. All rights reserved.
```
---
## ⚠️ 注意事项
### 1. 模板格式
- 模板使用 YAML 的多行字符串格式 (`|`)
- 支持多行注释
- 可以使用 `//``/* */` 格式
### 2. 变量替换
- 变量名必须使用大括号 `{variableName}`
- 变量名区分大小写
- 未定义的变量会被替换为空字符串
### 3. 兼容性
- 如果不配置 `templates.file_header`,会使用默认模板
- 如果配置的模板格式不正确,会尝试添加默认格式
### 4. 文件类型
- `{fileType}` 会根据文件类型自动设置
- `{fileName}` 需要显式传入(在生成时自动传入)
---
## 💡 最佳实践
### 1. 统一格式
```yaml
# ✅ 推荐:在项目根目录的 generator_config.yaml 中统一配置
templates:
file_header: |
// {fileType}
// Generated by {generatorName}
// {copyright}
```
### 2. 包含必要信息
```yaml
# ✅ 推荐:包含文件类型、来源、生成器信息
file_header: |
// {fileType}
// Source: {swaggerUrl}
// Generated by {generatorName} by {author}
```
### 3. 符合公司规范
```yaml
# ✅ 推荐:符合公司代码规范
generator:
copyright: "Copyright (C) 2025 Your Company. All rights reserved."
templates:
file_header: |
// {fileType}
// {copyright}
```
### 4. 简洁明了
```yaml
# ✅ 推荐:简洁但包含关键信息
file_header: |
// {fileType} - Generated by {generatorName}
```
---
## ✨ 总结
**已完成**:
- ✅ 添加 `templates.file_header` 配置项支持
- ✅ 实现模板变量替换(`{fileName}`, `{fileType}`, `{swaggerUrl}`, `{generatorName}`, `{author}`, `{copyright}`
- ✅ 从配置读取生成器信息(`generator.name`, `generator.author`, `generator.copyright`
- ✅ 支持默认模板(当配置不存在时)
- ✅ 更新所有生成器使用配置的文件头
**功能**:
- ✅ 完全可配置的文件头格式
- ✅ 模板变量支持
- ✅ 自动从配置读取生成器信息
- ✅ 向后兼容(默认模板)
**状态**: ✅ **功能完成,可以使用**
现在可以通过 `generator_config.yaml` 完全自定义生成的文件头格式了!

View File

@ -18,7 +18,6 @@
- **Dio + Retrofit 集成**:完美适配主流网络架构 - **Dio + Retrofit 集成**:完美适配主流网络架构
- **类型安全**:生成强类型的 API 接口和模型 - **类型安全**:生成强类型的 API 接口和模型
- **JSON 序列化**:自动生成 json_serializable 代码 - **JSON 序列化**:自动生成 json_serializable 代码
- **String 默认值**:自动为 String 类型字段添加默认值,提升容错性
- **文件上传支持**:完整的 multipart/form-data 支持 - **文件上传支持**:完整的 multipart/form-data 支持
### 🔧 高级特性 ### 🔧 高级特性
@ -43,57 +42,12 @@
## 🚀 快速开始 ## 🚀 快速开始
### 📦 作为 dev_dependencies 使用(推荐) ### 1. 安装依赖
本工具可以作为 `dev_dependencies` 集成到您的 Flutter/Dart 项目中:
#### 1. 添加依赖
在您的项目 `pubspec.yaml` 中添加:
```yaml
dev_dependencies:
swagger_generator_flutter:
git:
url: https://github.com/your-org/swagger_generator_flutter.git
ref: develop
# 或使用本地路径(开发时)
# swagger_generator_flutter:
# path: ../swagger_generator_flutter
```
#### 2. 创建配置文件
复制 [`generator_config.template.yaml`](./generator_config.template.yaml) 到您的项目根目录,重命名为 `generator_config.yaml` 并修改配置。
#### 3. 执行代码生成
```bash
# 安装依赖
flutter pub get
# 生成所有文件
dart run swagger_generator_flutter generate --all
# 生成 .g.dart 文件
dart run build_runner build --delete-conflicting-outputs
```
📖 **详细使用说明**:查看 [作为 dev_dependencies 使用指南](./USAGE_AS_DEV_DEPENDENCY.md)
---
### 💻 独立项目使用
如果您想直接在本项目中开发和测试:
#### 1. 安装依赖
```bash ```bash
flutter pub get flutter pub get
``` ```
#### 2. 基础用法(命令行) ### 2. 基础用法(命令行)
```bash ```bash
# 生成模型和API推荐 # 生成模型和API推荐
sh run_swagger.sh all sh run_swagger.sh all

View File

@ -1,293 +0,0 @@
# ✅ ApiPaths 生成功能移除总结
## 📋 移除目标
移除 `ApiPaths` 文件(`api_paths.dart`)的生成功能,简化代码生成器。
**移除日期**: 2025-11-05
**状态**: ✅ 已完成
---
## ✅ 已移除的内容
### 1. 命令选项
**移除前**:
```dart
const CommandOption(
name: 'endpoints',
shortName: 'e',
description: '生成API端点常量',
type: OptionType.flag,
),
```
**移除后**: ✅ 已删除
### 2. 生成逻辑
**移除前**:
```dart
// 生成端点代码
if (options.generateEndpoints) {
progress('正在生成API端点常量...');
final generator = EndpointCodeGenerator(document);
final code = generator.generate();
final filePath = '$fullOutputDir/api_paths.dart';
await FileUtils.writeFile(filePath, code);
success('API端点常量已保存到: $filePath');
generatedFiles++;
}
```
**移除后**: ✅ 已删除
### 3. 生成选项类
**移除前**:
```dart
class GenerateOptions {
final bool generateEndpoints;
final bool generateModels;
// ...
}
```
**移除后**:
```dart
class GenerateOptions {
final bool generateModels;
final bool generateDocs;
final bool generateApi;
// ...
}
```
### 4. 导入语句
**移除前**:
```dart
import '../generators/endpoint_code_generator.dart';
```
**移除后**: ✅ 已删除
### 5. 配置相关
**移除前**:
```dart
static const String defaultEndpointsFile = 'generated_api_paths.dart';
static const Map<String, dynamic> defaultGenerateOptions = {
'generateEndpoints': true,
// ...
};
```
**移除后**: ✅ 已删除
### 6. 类型验证器
**移除前**:
```dart
enum CodeType { model, endpoints, documentation }
case CodeType.endpoints:
// 验证逻辑
break;
```
**移除后**:
```dart
enum CodeType { model, documentation }
```
### 7. 未使用的方法
**移除**: `_detectApiVersion()` 方法(未使用)
---
## 📁 文件变更清单
### 修改的文件
1. ✅ `lib/commands/generate_command.dart`
- 移除 `--endpoints` 选项
- 移除端点生成逻辑
- 移除 `EndpointCodeGenerator` 导入
- 更新 `GenerateOptions`
- 移除 `_detectApiVersion()` 方法
2. ✅ `lib/core/config.dart`
- 移除 `defaultEndpointsFile` 常量
- 移除 `generateEndpoints` 配置项
3. ✅ `lib/utils/type_validator.dart`
- 移除 `CodeType.endpoints` 枚举值
- 移除相关验证逻辑
4. ✅ `USAGE_AS_DEV_DEPENDENCY.md`
- 移除 `--endpoints` 选项说明
- 移除 `api_paths.dart` 文件结构说明
### 保留的文件(未修改)
- `lib/generators/endpoint_code_generator.dart` - 生成器类文件保留(未使用)
- `lib/utils/string_utils.dart` - `generateEndpointName()` 方法保留(可能被其他功能使用)
---
## 📊 移除效果
### 代码简化
| 指标 | 移除前 | 移除后 | 改善 |
|------|--------|--------|------|
| 命令选项 | 8 个 | 7 个 | ✅ 减少 1 个 |
| 生成选项字段 | 6 个 | 5 个 | ✅ 减少 1 个 |
| 生成逻辑块 | 4 个 | 3 个 | ✅ 减少 1 个 |
| 配置项 | 6 个 | 5 个 | ✅ 减少 1 个 |
### 生成的文件
**移除前**:
```
generator/
├── api_paths.dart ❌ 不再生成
├── api/
├── api_models/
└── api_documentation.md
```
**移除后**:
```
generator/
├── api/
├── api_models/
└── api_documentation.md
```
---
## ✅ 保留的功能
### 完全保留
1. ✅ **API 接口生成** (`--api`)
- Retrofit 风格 API 接口
- 版本化 API 支持
2. ✅ **数据模型生成** (`--models`)
- 请求模型
- 响应模型
- 参数模型
- 枚举类型
3. ✅ **API 文档生成** (`--docs`)
- Markdown 格式文档
- 完整的 API 说明
---
## 📋 命令选项更新
### 移除前
```bash
dart run swagger_generator_flutter generate --endpoints # ❌ 已移除
dart run swagger_generator_flutter generate --models
dart run swagger_generator_flutter generate --api
dart run swagger_generator_flutter generate --docs
```
### 移除后
```bash
dart run swagger_generator_flutter generate --models
dart run swagger_generator_flutter generate --api
dart run swagger_generator_flutter generate --docs
dart run swagger_generator_flutter generate --all # 生成所有(不包含 endpoints
```
---
## 🎯 使用建议
### 替代方案
如果需要 API 路径常量,可以考虑:
1. **使用生成的 API 接口**
- 生成的 Retrofit API 接口已经包含了路径信息
- 路径在 `@GET/@POST/@PUT/@DELETE` 注解中
2. **手动定义常量**
```dart
class ApiPaths {
static const String getUser = '/api/v1/users';
static const String createUser = '/api/v1/users';
}
```
3. **从生成的 API 接口提取**
- 使用代码分析工具从生成的 API 文件中提取路径
---
## ✅ 测试验证
### 测试场景
1. ✅ **生成所有文件** - 正常工作,不生成 `api_paths.dart`
2. ✅ **生成 API** - 正常工作
3. ✅ **生成模型** - 正常工作
4. ✅ **生成文档** - 正常工作
5. ✅ **命令帮助** - 不显示 `--endpoints` 选项
### 测试命令
```bash
cd example/as_dev_dependency
dart run swagger_generator_flutter generate --help
dart run swagger_generator_flutter generate --all
```
**结果**: ✅ 正常工作,不再生成 `api_paths.dart`
---
## 📚 相关文件
### 已修改
1. ✅ `lib/commands/generate_command.dart`
2. ✅ `lib/core/config.dart`
3. ✅ `lib/utils/type_validator.dart`
4. ✅ `USAGE_AS_DEV_DEPENDENCY.md`
### 未修改(保留)
- `lib/generators/endpoint_code_generator.dart` - 生成器类(未使用,可后续删除)
- `lib/utils/string_utils.dart` - `generateEndpointName()` 方法(可能被其他功能使用)
---
## ✨ 总结
**移除完成**:
- ✅ 移除了 `--endpoints` 命令选项
- ✅ 移除了端点生成逻辑
- ✅ 移除了相关配置项
- ✅ 更新了文档
**保留功能**:
- ✅ API 接口生成
- ✅ 数据模型生成
- ✅ API 文档生成
**状态**: ✅ **移除完成,功能正常**
现在代码生成器更加精简,不再生成 `ApiPaths` 文件!

View File

@ -1,353 +0,0 @@
# 作为 dev_dependencies 使用指南
本工具可以作为 `dev_dependencies` 集成到您的 Flutter/Dart 项目中,实现自动化的 Swagger API 代码生成。
## 📦 安装步骤
### 1. 添加依赖
在您的项目的 `pubspec.yaml` 中添加:
```yaml
dev_dependencies:
swagger_generator_flutter:
git:
url: https://github.com/your-org/swagger_generator_flutter.git
ref: develop # 或者指定特定的 tag/branch
# 或者使用本地路径(开发时)
# swagger_generator_flutter:
# path: ../swagger_generator_flutter
```
### 2. 安装依赖包
```bash
flutter pub get
# 或
dart pub get
```
## 📝 配置文件
在您的项目根目录创建 `generator_config.yaml` 配置文件:
```yaml
# Swagger 代码生成器配置文件
# 基本配置
generator:
name: "my_project_generator"
version: "1.0"
author: "Your Name"
copyright: "Copyright (C) 2025 Your Company. All rights reserved."
# 输入配置
input:
# Swagger 文档源(支持多版本)
swagger_urls:
# 简写形式:直接列出 URL
- "https://your-api.com/swagger/v1/swagger.json"
- "https://your-api.com/swagger/v2/swagger.json"
# 完整形式:可以控制每个版本的启用状态
# - url: "https://your-api.com/swagger/v1/swagger.json"
# enabled: true # 可选,是否启用此版本(默认: true
# 验证配置
validate_schema: true
strict_mode: true
# 输出配置
output:
# 输出目录
base_dir: "./lib/generated"
api_dir: "./lib/generated/api"
models_dir: "./lib/generated/api_models"
# 文件命名
api_file_suffix: "_api.dart"
model_file_suffix: ".dart"
# 是否按 tag 分组
split_by_tags: true
# 代码生成配置
generation:
# API 接口配置
api:
enabled: true
use_retrofit: true
use_dio: true
parser: "JsonSerializable"
# 基础类型配置(根据您的项目调整)
base_result_type: "BaseResult"
base_page_result_type: "BasePageResult"
base_result_import: "package:your_project/common/models/base_result.dart"
base_page_result_import: "package:your_project/common/models/base_page_result.dart"
# 方法命名
method_naming: "camelCase"
# 数据模型配置
models:
enabled: true
use_json_serializable: true
# JsonSerializable 配置
json_serializable:
checked: true
include_if_null: false
explicit_to_json: true
# 类命名
class_naming: "PascalCase"
field_naming: "camelCase"
# 构造函数配置
use_const_constructor: true
required_for_non_nullable: true
```
## 🚀 使用方法
### 方式 1: 使用命令行直接执行
```bash
# 生成所有文件API、模型、文档
dart run swagger_generator_flutter generate --all
# 只生成模型
dart run swagger_generator_flutter generate --models
# 只生成 API 接口
dart run swagger_generator_flutter generate --api
# 只生成文档
dart run swagger_generator_flutter generate --docs
# 指定输出目录
dart run swagger_generator_flutter generate --all --output-dir lib/generated
```
### 方式 2: 创建便捷脚本
在项目根目录创建 `generate_api.sh` (macOS/Linux):
```bash
#!/bin/bash
echo "🚀 开始生成 API 代码..."
dart run swagger_generator_flutter generate --all
echo "✅ 代码生成完成!"
```
或创建 `generate_api.bat` (Windows):
```batch
@echo off
echo 🚀 开始生成 API 代码...
dart run swagger_generator_flutter generate --all
echo ✅ 代码生成完成!
```
给脚本添加执行权限并运行:
```bash
# macOS/Linux
chmod +x generate_api.sh
./generate_api.sh
# Windows
generate_api.bat
```
### 方式 3: 集成到构建流程
`pubspec.yaml` 中添加自定义脚本:
```yaml
# 可以在 scripts 字段添加(如果使用 melos 或其他工具)
scripts:
generate: dart run swagger_generator_flutter generate --all
```
## 📋 命令选项
| 选项 | 简写 | 说明 |
|------|------|------|
| `--all` | `-a` | 生成所有文件(默认) |
| `--api` | `-r` | 生成 Retrofit 风格 API 接口 |
| `--models` | `-m` | 生成数据模型 |
| `--docs` | `-d` | 生成 API 文档 |
| `--simple` | `-s` | 使用简洁版模型生成 |
| `--split-by-tags` | `-t` | 按 tags 分组生成多个 API 文件 |
| `--output-dir` | `-o` | 指定输出目录 |
| `--help` | `-h` | 显示帮助信息 |
## 📂 生成的文件结构
执行生成命令后,将在输出目录创建以下结构:
```
lib/generated/
├── api/
│ ├── v1/
│ │ ├── user_api.dart
│ │ ├── product_api.dart
│ │ └── index.dart
│ ├── v2/
│ │ ├── user_api_v2.dart
│ │ └── index.dart
│ └── api_client.dart
├── api_models/
│ ├── request/
│ │ ├── create_user_request.dart
│ │ └── index.dart
│ ├── result/
│ │ ├── user_result.dart
│ │ └── index.dart
│ ├── parameters/
│ │ ├── get_user_list_parameters.dart
│ │ └── index.dart
│ └── index.dart
├── api_documentation.md
└── SUMMARY.md
```
## 🔧 必需的项目依赖
确保您的项目 `pubspec.yaml` 包含以下依赖:
```yaml
dependencies:
# HTTP 客户端
dio: ^5.0.0
retrofit: ^4.0.0
# JSON 序列化
json_annotation: ^4.8.0
dev_dependencies:
# 代码生成
build_runner: ^2.4.7
retrofit_generator: ^8.0.0
json_serializable: ^6.7.1
```
## 🔄 运行 build_runner
生成代码后,需要运行 `build_runner` 生成 `.g.dart` 文件:
```bash
# 一次性构建
dart run build_runner build --delete-conflicting-outputs
# 监听模式(开发时推荐)
dart run build_runner watch --delete-conflicting-outputs
```
## 💡 工作流程示例
完整的代码生成工作流程:
```bash
# 1. 生成 API 代码
dart run swagger_generator_flutter generate --all
# 2. 运行 build_runner 生成序列化代码
dart run build_runner build --delete-conflicting-outputs
# 3. 格式化代码(可选)
dart format lib/generated
# 4. 分析代码(可选)
dart analyze lib/generated
```
## 🎯 CI/CD 集成
在您的 CI/CD 流程中添加代码生成步骤:
### GitHub Actions 示例
```yaml
name: Generate API Code
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Generate API code
run: dart run swagger_generator_flutter generate --all
- name: Run build_runner
run: dart run build_runner build --delete-conflicting-outputs
- name: Check for changes
run: |
git diff --exit-code || (echo "Generated code has changes" && exit 1)
```
## 🐛 常见问题
### 1. 找不到命令
**问题**: `Error: Could not find package swagger_generator_flutter`
**解决**: 确保已运行 `flutter pub get` 安装依赖
### 2. 配置文件未找到
**问题**: 工具无法读取 `generator_config.yaml`
**解决**: 确保配置文件在项目根目录,与 `pubspec.yaml` 同级
### 3. 生成的代码有编译错误
**问题**: 生成的代码缺少导入或有类型错误
**解决**:
- 检查 `generator_config.yaml` 中的导入配置
- 确保项目中已定义 `BaseResult` 等基础类型
- 运行 `dart run build_runner build --delete-conflicting-outputs`
### 4. 权限问题
**问题**: 无法写入生成的文件
**解决**:
- 检查输出目录权限
- 确保输出目录存在或工具有创建权限
## 📚 更多资源
- [完整文档](docs/USAGE_GUIDE.md)
- [API 参考](docs/API_REFERENCE.md)
- [项目概览](docs/PROJECT_OVERVIEW.md)
- [示例项目](example/)
## 🤝 贡献
如果您发现问题或有改进建议,欢迎提交 Issue 或 Pull Request。
## 📄 许可证
Copyright (C) 2025 YuanXuan. All rights reserved.

View File

@ -1,8 +0,0 @@
#!/usr/bin/env dart
// Swagger Generator Flutter
//
// `dart run swagger_generator_flutter`
import 'main.dart' as entry;
void main(List<String> arguments) => entry.main(arguments);

View File

@ -1,544 +1,457 @@
# API 参考文档 # API 参考文档
## 📚 核心 API 类库 本文档详细介绍了 Swagger Generator Flutter 的所有 API 接口和配置选项。
### 🔧 解析器 (Parsers) ## 目录
#### PerformanceParser - [核心类](#核心类)
- [生成器](#生成器)
- [解析器](#解析器)
- [验证器](#验证器)
- [缓存系统](#缓存系统)
- [配置选项](#配置选项)
高性能 OpenAPI 文档解析器,支持并行处理和性能监控。 ## 核心类
### SwaggerDocument
OpenAPI 文档的主要数据模型。
```dart ```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; class SwaggerDocument {
final String title;
final String version;
final String description;
final List<ApiServer> servers;
final Map<String, ApiPath> paths;
final Map<String, ApiModel> models;
final ApiComponents components;
final List<ApiSecurityRequirement> security;
// 创建解析器 SwaggerDocument({
final parser = PerformanceParser( required this.title,
config: ParseConfig( required this.version,
enablePerformanceStats: true, // 启用性能统计 required this.description,
enableParallelParsing: true, // 启用并行解析 required this.servers,
enableCaching: true, // 启用缓存 required this.paths,
maxConcurrency: 4, // 最大并发数 required this.models,
enableMemoryOptimization: true, // 内存优化 required this.components,
), required this.security,
); });
// 解析文档 factory SwaggerDocument.fromJson(Map<String, dynamic> json);
final jsonString = await File('swagger.json').readAsString(); Map<String, dynamic> toJson();
final document = await parser.parseDocument(jsonString); }
// 获取性能统计
final stats = parser.lastStats;
print('解析时间: ${stats?.totalTime.inMilliseconds}ms');
print('路径数量: ${stats?.pathCount}');
print('吞吐量: ${stats?.bytesPerSecond.toStringAsFixed(2)} bytes/s');
``` ```
**主要方法:** ### ApiPath
| 方法 | 描述 | 返回类型 | API 路径和操作的定义。
|------|------|----------|
| `parseDocument(String jsonString)` | 解析 OpenAPI 文档 | `Future<SwaggerDocument>` |
| `parseDocumentFromFile(String filePath)` | 从文件解析文档 | `Future<SwaggerDocument>` |
| `validateAndParse(String jsonString)` | 验证并解析文档 | `Future<SwaggerDocument>` |
**配置选项:** ```dart
class ApiPath {
final String path;
final HttpMethod method;
final String summary;
final String description;
final String operationId;
final List<String> tags;
final List<ApiParameter> parameters;
final ApiRequestBody? requestBody;
final Map<String, ApiResponse> responses;
final List<ApiSecurityRequirement> security;
ApiPath({
required this.path,
required this.method,
required this.summary,
required this.description,
required this.operationId,
required this.tags,
required this.parameters,
this.requestBody,
required this.responses,
required this.security,
});
}
```
### ApiModel
数据模型的定义。
```dart
class ApiModel {
final String name;
final String description;
final Map<String, ApiProperty> properties;
final List<String> required;
ApiModel({
required this.name,
required this.description,
required this.properties,
required this.required,
});
}
```
## 生成器
### BaseGenerator
所有生成器的基类。
```dart
abstract class BaseGenerator {
String get generatorType;
String generate();
}
```
### RetrofitApiGenerator
基础的 Retrofit API 生成器。
```dart
class RetrofitApiGenerator extends BaseGenerator {
final String className;
final bool splitByTags;
final bool useRetrofit;
final bool generateModels;
RetrofitApiGenerator({
this.className = 'ApiService',
this.splitByTags = false,
this.useRetrofit = true,
this.generateModels = true,
});
@override
String generateFromDocument(SwaggerDocument document);
}
```
#### 方法
- `generateFromDocument(SwaggerDocument document)`: 从文档生成代码
- `generateSingleApiFile()`: 生成单个 API 文件
- `generateMainApiFile()`: 生成主 API 文件(分模块时)
### OptimizedRetrofitGenerator
优化版的 Retrofit API 生成器。
```dart
class OptimizedRetrofitGenerator extends BaseGenerator {
final String className;
final bool generateModularApis;
final bool generateBaseResult;
final bool generatePagination;
final bool generateFileUpload;
final String baseResultType;
final String pageResultType;
OptimizedRetrofitGenerator({
this.className = 'ApiService',
this.generateModularApis = true,
this.generateBaseResult = true,
this.generatePagination = true,
this.generateFileUpload = true,
this.baseResultType = 'BaseResult',
this.pageResultType = 'BasePageResult',
});
}
```
#### 特性
- **模块化生成**: 按 API 标签自动分组
- **基础类型**: 生成 BaseResult、BasePageResult 等
- **文件上传**: 支持 multipart/form-data
- **工具类**: 生成 ApiUtils 工具类
### PerformanceGenerator
高性能代码生成器。
```dart
class PerformanceGenerator extends BaseGenerator {
final int maxConcurrency;
final bool enableCaching;
final bool enableIncremental;
final bool enableParallel;
PerformanceGenerator({
this.maxConcurrency = 4,
this.enableCaching = true,
this.enableIncremental = true,
this.enableParallel = true,
});
Future<String> generateFromDocument(SwaggerDocument document);
GenerationStats getStats();
CacheStats getCacheStats();
void clearCache();
}
```
#### 方法
- `generateFromDocument()`: 异步生成代码
- `getStats()`: 获取生成性能统计
- `getCacheStats()`: 获取缓存统计
- `clearCache()`: 清除缓存
## 解析器
### PerformanceParser
高性能 OpenAPI 解析器。
```dart
class PerformanceParser {
final ParseConfig config;
PerformanceParser({ParseConfig? config});
Future<SwaggerDocument> parseDocument(String jsonString);
ParsePerformanceStats? get lastStats;
void clearCache();
Map<String, dynamic> getCacheStats();
}
```
### ParseConfig
解析器配置。
```dart ```dart
class ParseConfig { class ParseConfig {
final bool enablePerformanceStats; // 启用性能统计 final bool enableParallelParsing;
final bool enableParallelParsing; // 启用并行解析 final bool enableStreamParsing;
final bool enableStreamParsing; // 启用流式解析 final bool enableIncrementalParsing;
final bool enableCaching; // 启用缓存 final bool enableCaching;
final int maxConcurrency; // 最大并发数 final int maxConcurrency;
final bool enableMemoryOptimization; // 内存优化 final int streamBufferSize;
final Duration cacheTimeout; // 缓存超时时间 final bool enablePerformanceStats;
final bool enableMemoryOptimization;
const ParseConfig({
this.enableParallelParsing = true,
this.enableStreamParsing = false,
this.enableIncrementalParsing = false,
this.enableCaching = true,
this.maxConcurrency = 4,
this.streamBufferSize = 8192,
this.enablePerformanceStats = false,
this.enableMemoryOptimization = true,
});
} }
``` ```
--- ## 验证器
### 🏭 生成器 (Generators) ### EnhancedValidator
#### OptimizedRetrofitGenerator 增强的文档验证器。
优化的 Retrofit API 代码生成器,专为企业级项目设计。
```dart ```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; class EnhancedValidator {
final bool strictMode;
final bool includeWarnings;
// 创建生成器 EnhancedValidator({
final generator = OptimizedRetrofitGenerator( this.strictMode = false,
className: 'ApiService', // API 服务类名 this.includeWarnings = true,
generateModularApis: true, // 生成模块化 API });
generateBaseResult: true, // 生成基础响应类型
generatePagination: true, // 生成分页支持
generateFileUpload: true, // 生成文件上传支持
baseResultType: 'BaseResult', // 基础响应类型名
pageResultType: 'BasePageResult', // 分页响应类型名
);
// 生成代码 bool validateDocument(SwaggerDocument document);
final generatedCode = generator.generateFromDocument(document); ErrorReporter get errorReporter;
// 保存到文件
await File('lib/api/api_service.dart').writeAsString(generatedCode);
```
**主要方法:**
| 方法 | 描述 | 返回类型 |
|------|------|----------|
| `generateFromDocument(SwaggerDocument doc)` | 从文档生成代码 | `String` |
| `generateModularApis(SwaggerDocument doc)` | 生成模块化 API | `Map<String, String>` |
| `generateModels(SwaggerDocument doc)` | 生成数据模型 | `Map<String, String>` |
| `generateUtils(SwaggerDocument doc)` | 生成工具类 | `String` |
**配置选项:**
```dart
class OptimizedRetrofitGenerator {
final String className; // 生成的类名
final bool generateModularApis; // 是否生成模块化 API
final bool generateBaseResult; // 是否生成基础响应类型
final bool generatePagination; // 是否生成分页支持
final bool generateFileUpload; // 是否生成文件上传支持
final String baseResultType; // 基础响应类型名
final String pageResultType; // 分页响应类型名
final List<String> excludeTags; // 排除的标签
final Map<String, String> typeMapping; // 类型映射
} }
``` ```
#### PerformanceGenerator ### ErrorReporter
高性能代码生成器,支持并发生成和增量更新。 错误报告器。
```dart ```dart
final generator = PerformanceGenerator( class ErrorReporter {
maxConcurrency: 4, // 最大并发数 List<DetailedError> get errors;
enableCaching: true, // 启用缓存 bool get hasErrors;
enableIncremental: true, // 启用增量生成 bool get hasCriticalErrors;
enableParallel: true, // 启用并行生成
cacheStrategy: CacheStrategy.smart, // 缓存策略
);
// 并行生成多个文件 void addError(DetailedError error);
final results = await generator.generateParallel(document, [ void reportError({
GenerationTask.apis, required String id,
GenerationTask.models, required String title,
GenerationTask.utils, required String description,
]); required ErrorSeverity severity,
``` required ErrorCategory category,
required String jsonPath,
// ... 其他参数
});
--- List<DetailedError> getErrorsBySeverity(ErrorSeverity severity);
List<DetailedError> getErrorsByCategory(ErrorCategory category);
Map<ErrorSeverity, int> getErrorStatistics();
### ✅ 验证器 (Validators) String generateReport({
bool includeStatistics = true,
bool groupByCategory = false,
ErrorSeverity? minSeverity,
});
#### EnhancedValidator String generateJsonReport();
void clear();
增强型文档验证器,提供详细的错误报告和修复建议。
```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart';
// 创建验证器
final validator = EnhancedValidator(
strictMode: false, // 严格模式
includeWarnings: true, // 包含警告
enableAutoFix: true, // 启用自动修复
customRules: [ // 自定义验证规则
RequiredFieldRule(),
NamingConventionRule(),
],
);
// 验证文档
final isValid = validator.validateDocument(document);
// 获取错误报告
final errors = validator.errorReporter.getErrorsBySeverity(ErrorSeverity.error);
final warnings = validator.errorReporter.getErrorsBySeverity(ErrorSeverity.warning);
// 生成详细报告
final report = validator.errorReporter.generateReport();
print(report);
```
**错误级别:**
```dart
enum ErrorSeverity {
critical, // 严重错误,阻止生成
error, // 错误,可能影响生成质量
warning, // 警告,建议修复
info, // 信息,仅供参考
} }
``` ```
**内置验证规则:** ## 缓存系统
| 规则 | 描述 | 级别 | ### SmartCache
|------|------|------|
| `SchemaValidationRule` | Schema 定义验证 | Error |
| `ReferenceValidationRule` | 引用完整性验证 | Critical |
| `NamingConventionRule` | 命名规范验证 | Warning |
| `TypeConsistencyRule` | 类型一致性验证 | Error |
| `RequiredFieldRule` | 必填字段验证 | Warning |
--- 智能缓存管理器。
### 🗄️ 缓存管理 (Cache)
#### SmartCache
智能缓存管理器,支持多级缓存和自动清理。
```dart ```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; class SmartCache<T> {
final int maxSize;
final CacheStrategy strategy;
final Duration defaultTtl;
// 创建缓存 SmartCache({
final cache = SmartCache<String>( int maxSize = 1000,
maxSize: 1000, // 最大缓存大小 CacheStrategy strategy = CacheStrategy.smart,
strategy: CacheStrategy.smart, // 缓存策略 Duration defaultTtl = const Duration(hours: 1),
defaultTtl: Duration(hours: 1), // 默认过期时间 });
enablePersistence: true, // 启用持久化
);
// 使用缓存 T? get(String key);
cache.put('key', 'value', ttl: Duration(minutes: 30)); void put(String key, T value, {Duration? ttl, String? etag});
final value = cache.get('key'); bool containsKey(String key);
T? remove(String key);
void clear();
// 获取统计信息 CacheStats getStats();
final stats = cache.getStats(); List<String> getKeysNeedingRefresh();
print('缓存命中率: ${(stats.hitRate * 100).toStringAsFixed(1)}%'); Future<void> refreshKeys(List<String> keys, Future<T> Function(String key) refreshFunction);
print('内存使用: ${stats.memoryUsage}'); Future<void> warmUp(Map<String, Future<T> Function()> warmUpFunctions);
}
``` ```
**缓存策略:** ### CacheStrategy
缓存策略枚举。
```dart ```dart
enum CacheStrategy { enum CacheStrategy {
lru, // LRU (最近最少使用) lru, // 最近最少使用
lfu, // LFU (最少使用频率) lfu, // 最近最常使用
fifo, // FIFO (先进先出) fifo, // 先进先出
smart, // 智能策略 (结合多种算法) ttl, // 基于时间的过期
smart, // 智能策略
} }
``` ```
--- ## 配置选项
### 🔧 工具类 (Utils) ### 生成器配置
#### StringUtils | 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `className` | String | 'ApiService' | 生成的主类名 |
| `splitByTags` | bool | true | 是否按标签分割 API |
| `generateModularApis` | bool | true | 生成模块化 API |
| `generateBaseResult` | bool | true | 生成基础响应类型 |
| `generatePagination` | bool | true | 生成分页支持 |
| `generateFileUpload` | bool | true | 生成文件上传支持 |
| `baseResultType` | String | 'BaseResult' | 基础响应类型名 |
| `pageResultType` | String | 'BasePageResult' | 分页响应类型名 |
字符串处理工具类,提供命名转换和格式化功能。 ### 解析器配置
| 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `enableParallelParsing` | bool | true | 启用并行解析 |
| `enableStreamParsing` | bool | false | 启用流式解析 |
| `enableCaching` | bool | true | 启用缓存 |
| `maxConcurrency` | int | 4 | 最大并发数 |
| `enablePerformanceStats` | bool | false | 启用性能统计 |
### 验证器配置
| 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `strictMode` | bool | false | 严格模式 |
| `includeWarnings` | bool | true | 包含警告 |
## 错误类型
### ErrorSeverity
```dart ```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; enum ErrorSeverity {
info, // 信息
// 命名转换 warning, // 警告
final camelCase = StringUtils.toCamelCase('user_name'); // userName error, // 错误
final pascalCase = StringUtils.toPascalCase('user_name'); // UserName critical, // 严重错误
final snakeCase = StringUtils.toSnakeCase('userName'); // user_name }
// 类型转换
final dartType = StringUtils.openApiTypeToDart('integer'); // int
final nullableType = StringUtils.makeNullable('String'); // String?
// 文档注释生成
final comment = StringUtils.generateDocComment(
'User login endpoint',
parameters: ['username', 'password'],
returns: 'LoginResult',
);
``` ```
#### FileUtils ### ErrorCategory
文件操作工具类,提供安全的文件读写功能。
```dart ```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; enum ErrorCategory {
syntax, // 语法错误
// 安全写入文件 schema, // Schema 错误
await FileUtils.writeStringToFile( reference, // 引用错误
'lib/api/generated_api.dart', validation, // 验证错误
generatedCode, compatibility, // 兼容性问题
createDirs: true, // 自动创建目录 performance, // 性能问题
backup: true, // 创建备份 security, // 安全问题
); bestPractice, // 最佳实践
}
// 批量写入文件
await FileUtils.writeMultipleFiles({
'lib/api/user_api.dart': userApiCode,
'lib/api/order_api.dart': orderApiCode,
'lib/models/user.dart': userModelCode,
});
// 清理生成的文件
await FileUtils.cleanGeneratedFiles('lib/api/generated/');
``` ```
--- ## 性能统计
### 📊 性能监控 (Performance) ### ParsePerformanceStats
#### PerformanceMonitor 解析性能统计。
性能监控器,提供详细的性能统计和分析。
```dart ```dart
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; class ParsePerformanceStats {
final Duration totalTime;
final Duration parseTime;
final Duration validationTime;
final Duration modelCreationTime;
final int memoryUsage;
final int documentSize;
final int pathCount;
final int schemaCount;
// 创建监控器 double get pathsPerSecond;
final monitor = PerformanceMonitor(); double get schemasPerSecond;
double get bytesPerSecond;
// 开始监控 }
monitor.startOperation('parse_document');
// ... 执行操作
monitor.endOperation('parse_document');
// 获取统计信息
final stats = monitor.getOperationStats('parse_document');
print('操作次数: ${stats.count}');
print('平均耗时: ${stats.averageTime.inMilliseconds}ms');
print('最大耗时: ${stats.maxTime.inMilliseconds}ms');
// 生成性能报告
final report = monitor.generateReport();
print(report);
``` ```
--- ### GenerationStats
## 🔄 完整使用流程 生成性能统计。
### 基本使用流程
```dart ```dart
import 'dart:io'; class GenerationStats {
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart'; final int totalTasks;
final int completedTasks;
final int failedTasks;
final Duration totalTime;
final Duration averageTaskTime;
final int linesGenerated;
final int bytesGenerated;
final double parallelEfficiency;
Future<void> generateApiCode() async { double get successRate;
// 1. 创建解析器 double get linesPerSecond;
final parser = PerformanceParser( double get bytesPerSecond;
config: ParseConfig(
enablePerformanceStats: true,
enableCaching: true,
),
);
// 2. 创建验证器
final validator = EnhancedValidator(
includeWarnings: true,
);
// 3. 创建生成器
final generator = OptimizedRetrofitGenerator(
className: 'ApiService',
generateModularApis: true,
generateBaseResult: true,
);
try {
// 4. 解析文档
final jsonString = await File('swagger.json').readAsString();
final document = await parser.parseDocument(jsonString);
// 5. 验证文档
final isValid = validator.validateDocument(document);
if (!isValid) {
final report = validator.errorReporter.generateReport();
print('验证失败:\n$report');
return;
}
// 6. 生成代码
final generatedCode = generator.generateFromDocument(document);
// 7. 保存文件
await File('lib/api/api_service.dart').writeAsString(generatedCode);
print('✅ 代码生成完成!');
// 8. 显示性能统计
final stats = parser.lastStats;
if (stats != null) {
print('解析时间: ${stats.totalTime.inMilliseconds}ms');
print('生成的路径数: ${stats.pathCount}');
}
} catch (e, stackTrace) {
print('❌ 生成失败: $e');
print('堆栈跟踪: $stackTrace');
}
} }
``` ```
### 高级使用流程 (企业级)
```dart
Future<void> generateEnterpriseApiCode() async {
// 1. 配置高性能解析器
final parser = PerformanceParser(
config: ParseConfig(
enablePerformanceStats: true,
enableParallelParsing: true,
enableCaching: true,
maxConcurrency: 8,
enableMemoryOptimization: true,
),
);
// 2. 配置增强验证器
final validator = EnhancedValidator(
strictMode: true,
includeWarnings: true,
enableAutoFix: true,
customRules: [
RequiredFieldRule(),
NamingConventionRule(),
TypeConsistencyRule(),
],
);
// 3. 配置性能生成器
final generator = PerformanceGenerator(
maxConcurrency: 4,
enableCaching: true,
enableIncremental: true,
enableParallel: true,
);
// 4. 配置智能缓存
final cache = SmartCache<SwaggerDocument>(
maxSize: 100,
strategy: CacheStrategy.smart,
defaultTtl: Duration(hours: 2),
);
try {
// 5. 解析和缓存文档
final cacheKey = 'swagger_document_v1';
var document = cache.get(cacheKey);
if (document == null) {
final jsonString = await File('swagger.json').readAsString();
document = await parser.parseDocument(jsonString);
cache.put(cacheKey, document);
}
// 6. 验证文档
final isValid = validator.validateDocument(document);
if (!isValid) {
final errors = validator.errorReporter
.getErrorsBySeverity(ErrorSeverity.critical);
if (errors.isNotEmpty) {
throw Exception('文档包含严重错误,无法继续生成');
}
}
// 7. 并行生成多个文件
final results = await generator.generateParallel(document, [
GenerationTask.apis,
GenerationTask.models,
GenerationTask.utils,
GenerationTask.documentation,
]);
// 8. 保存生成的文件
for (final entry in results.entries) {
final filePath = 'lib/api/generated/${entry.key}.dart';
await FileUtils.writeStringToFile(
filePath,
entry.value,
createDirs: true,
backup: true,
);
}
print('✅ 企业级代码生成完成!');
// 9. 生成性能报告
final performanceReport = parser.generatePerformanceReport();
await File('reports/performance_report.md')
.writeAsString(performanceReport);
// 10. 生成验证报告
final validationReport = validator.errorReporter.generateReport();
await File('reports/validation_report.md')
.writeAsString(validationReport);
} catch (e, stackTrace) {
print('❌ 企业级生成失败: $e');
print('堆栈跟踪: $stackTrace');
}
}
```
---
## 🔍 错误处理和调试
### 常见错误类型
```dart
// 解析错误
try {
final document = await parser.parseDocument(jsonString);
} on SwaggerParseException catch (e) {
print('解析错误: ${e.message}');
print('错误位置: ${e.location}');
print('修复建议: ${e.suggestion}');
}
// 验证错误
try {
final isValid = validator.validateDocument(document);
} on ValidationException catch (e) {
print('验证错误: ${e.message}');
print('错误字段: ${e.fieldPath}');
print('期望值: ${e.expectedValue}');
print('实际值: ${e.actualValue}');
}
// 生成错误
try {
final code = generator.generateFromDocument(document);
} on CodeGenerationException catch (e) {
print('生成错误: ${e.message}');
print('错误类型: ${e.errorType}');
print('相关对象: ${e.relatedObject}');
}
```
### 调试工具
```dart
// 启用调试模式
final parser = PerformanceParser(
config: ParseConfig(
enableDebugMode: true, // 启用调试模式
enableVerboseLogging: true, // 详细日志
logLevel: LogLevel.debug, // 日志级别
),
);
// 性能分析
final profiler = PerformanceProfiler();
profiler.startProfiling();
// ... 执行操作
final profile = profiler.endProfiling();
print('性能分析: ${profile.summary}');
// 内存分析
final memoryAnalyzer = MemoryAnalyzer();
final usage = memoryAnalyzer.getCurrentUsage();
print('内存使用: ${usage.totalMemory}MB');
print('缓存占用: ${usage.cacheMemory}MB');
```
---
**文档版本**: v2.0
**最后更新**: 2025-01-24
**维护者**: Max

View File

@ -1,176 +0,0 @@
# XY Swagger Generator - 项目概览
## 📋 项目简介
XY Swagger Generator 是一个专为 Flutter 开发优化的 OpenAPI 3.0 代码生成器,旨在自动化 API 接口和数据模型的生成过程,提升开发效率并确保代码质量。
### 🎯 设计目标
- **标准化优先**: 严格遵循 OpenAPI 3.0 规范,确保与后端 API 文档完全一致
- **类型安全**: 生成强类型 Dart 代码,在编译时发现潜在问题
- **高性能**: 支持大型 API 文档的高效解析和代码生成
- **企业级**: 提供完整的验证、缓存、监控和错误处理机制
## 🏗️ 核心架构
### 架构层次
```
┌─────────────────────────────────────────────────┐
│ 用户接口层 │
├─────────────────────────────────────────────────┤
│ 命令行工具 (CLI) │
├─────────────────────────────────────────────────┤
│ 生成器层 │
│ ┌─────────────┬─────────────┬─────────────┐ │
│ │ 基础 │ 优化 │ 性能 │ │
│ │ 生成器 │ 生成器 │ 生成器 │ │
│ └─────────────┴─────────────┴─────────────┘ │
├─────────────────────────────────────────────────┤
│ 验证层 │
│ ┌─────────────┬─────────────────────────────┐ │
│ │ Schema │ Enhanced │ │
│ │ Validator │ Validator │ │
│ └─────────────┴─────────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ 解析层 │
│ ┌─────────────┬─────────────────────────────┐ │
│ │ Swagger │ Performance │ │
│ │ Parser │ Parser │ │
│ └─────────────┴─────────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ 核心层 │
│ ┌─────────────┬─────────────┬─────────────┐ │
│ │ Models │ Cache │ Utils │ │
│ └─────────────┴─────────────┴─────────────┘ │
└─────────────────────────────────────────────────┘
```
### 核心组件
#### 1. 解析器 (Parsers)
- **SwaggerDataParser**: 基础 OpenAPI 文档解析
- **PerformanceParser**: 高性能解析器,支持并行处理和流式解析
#### 2. 验证器 (Validators)
- **SchemaValidator**: 基础 Schema 验证
- **EnhancedValidator**: 增强验证器,提供详细的错误报告
#### 3. 生成器 (Generators)
- **RetrofitApiGenerator**: 基础 Retrofit API 生成器
- **OptimizedRetrofitGenerator**: 优化版生成器,支持模块化和企业级特性
- **PerformanceGenerator**: 高性能生成器,支持并发和缓存
#### 4. 工具类 (Utils)
- **SmartCache**: 智能缓存管理
- **FileUtils**: 文件操作工具
- **StringUtils**: 字符串处理工具
- **TypeValidator**: 类型验证工具
## 🔧 技术特性
### 性能优化
- **并行解析**: 支持多线程解析大型 API 文档
- **智能缓存**: 基于 LRU 算法的多级缓存机制
- **增量生成**: 只更新变更的部分,避免全量重新生成
- **内存优化**: 流式处理,降低内存占用
### 代码质量
- **严格类型检查**: 基于 OpenAPI Schema 的强类型生成
- **代码规范**: 统一的命名规范和代码风格
- **错误处理**: 详细的错误诊断和修复建议
- **测试覆盖**: 完整的单元测试和集成测试
### 企业级特性
- **配置管理**: 灵活的配置选项和预设模板
- **版本控制**: 支持 API 版本管理和向后兼容性
- **监控统计**: 详细的性能统计和生成报告
- **扩展性**: 插件化架构,支持自定义扩展
## 📊 性能指标
### 解析性能
- **大型文档**: 支持 10MB+ 的 OpenAPI 文档
- **解析速度**: 平均 1000+ paths/second
- **内存效率**: 流式处理,内存占用 < 100MB
- **并发支持**: 最大 8 个并发解析任务
### 生成性能
- **代码生成**: 平均 500+ endpoints/second
- **文件操作**: 支持批量文件生成和原子操作
- **缓存命中率**: 智能缓存命中率 > 80%
- **增量更新**: 变更检测准确率 > 95%
## 🎯 应用场景
### 适用项目类型
- **企业级 Flutter 应用**: 大量 API 接口,需要标准化管理
- **多人协作项目**: 需要统一的代码风格和开发规范
- **快速迭代项目**: API 变更频繁,需要快速同步
- **微服务架构**: 多个服务的 API 需要统一管理
### 团队规模
- **小型团队** (2-5人): 提升开发效率,减少重复工作
- **中型团队** (5-20人): 统一开发标准,提升协作效率
- **大型团队** (20+人): 企业级管理,确保代码质量和一致性
## 🚀 核心优势
### 1. 开发效率提升
- **自动化程度**: 95% 的 API 代码自动生成
- **开发时间节省**: 减少 70%+ 的 API 相关开发时间
- **错误率降低**: 类型相关错误减少 90%+
### 2. 代码质量保证
- **类型安全**: 编译时类型检查,避免运行时错误
- **标准一致**: 统一的代码风格和命名规范
- **文档同步**: API 文档与代码自动保持同步
### 3. 维护成本降低
- **变更响应**: API 变更响应时间从小时级降到分钟级
- **技术债务**: 标准化代码结构,易于维护和扩展
- **团队协作**: 统一的开发流程和代码规范
## 📈 发展路线
### 当前版本 (v2.0.x)
- ✅ 完整的 OpenAPI 3.0 支持
- ✅ 高性能解析和生成
- ✅ 企业级验证和错误处理
- ✅ Dio + Retrofit 完美集成
### 下一版本 (v2.1.x)
- 🔄 GraphQL 支持
- 🔄 更多代码生成模板
- 🔄 可视化配置界面
- 🔄 CI/CD 集成工具
### 未来规划 (v3.0.x)
- 📋 多语言支持 (Kotlin, Swift)
- 📋 云端代码生成服务
- 📋 AI 辅助优化建议
- 📋 实时 API 监控
## 🤝 社区与支持
### 文档资源
- [快速开始指南](../QUICK_REFERENCE.md)
- [API 参考文档](./API_REFERENCE.md)
- [最佳实践指南](./BEST_PRACTICES.md)
- [故障排除指南](./TROUBLESHOOTING.md)
### 贡献方式
- [贡献指南](../CONTRIBUTING.md)
- [代码审查清单](../CODE_REVIEW_CHECKLIST.md)
- [开发环境搭建](./DEVELOPMENT_SETUP.md)
- [测试指南](./TESTING_GUIDE.md)
---
**项目维护者**: Max
**最后更新**: 2025-01-24
**文档版本**: v2.0

View File

@ -1,783 +0,0 @@
# 使用指南与最佳实践
## 🚀 快速开始
### 环境准备
1. **确保 Dart/Flutter 环境**
```bash
dart --version # 确保 Dart >= 3.0
flutter --version # 确保 Flutter >= 3.0
```
2. **安装项目依赖**
```bash
cd your_flutter_project
flutter pub get
```
3. **准备 OpenAPI 文档**
- 确保有有效的 `swagger.json``openapi.json` 文件
- 建议使用 OpenAPI 3.0 格式
### 基础使用
#### 命令行方式 (推荐新手)
```bash
# 克隆或下载项目
git clone <repository-url>
cd swagger_generator_flutter
# 安装依赖
flutter pub get
# 将你的 swagger.json 放在项目根目录
# 生成所有代码
sh run_swagger.sh all
# 或者分别生成
sh run_swagger.sh api # 只生成 API
sh run_swagger.sh models # 只生成模型
sh run_swagger.sh docs # 只生成文档
```
#### 编程方式 (推荐进阶用户)
```dart
import 'dart:io';
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart';
void main() async {
// 1. 创建解析器
final parser = PerformanceParser(
config: ParseConfig(
enablePerformanceStats: true,
enableCaching: true,
),
);
// 2. 解析文档
final jsonString = await File('swagger.json').readAsString();
final document = await parser.parseDocument(jsonString);
// 3. 创建生成器
final generator = OptimizedRetrofitGenerator(
className: 'ApiService',
generateModularApis: true,
generateBaseResult: true,
);
// 4. 生成并保存代码
final code = generator.generateFromDocument(document);
await File('lib/api/api_service.dart').writeAsString(code);
print('✅ 代码生成完成!');
}
```
---
## 🏗️ 项目集成
### 1. 依赖配置
在你的 Flutter 项目的 `pubspec.yaml` 中添加必要依赖:
```yaml
dependencies:
# 网络请求
dio: ^5.4.0
retrofit: ^4.0.0
# JSON 序列化
json_annotation: ^4.8.1
# 其他依赖
logging: ^1.2.0
dev_dependencies:
# 代码生成
build_runner: ^2.4.7
retrofit_generator: ^8.0.0
json_serializable: ^6.7.1
# 测试
test: ^1.24.0
```
### 2. 项目结构
推荐的项目结构:
```
your_flutter_project/
├── lib/
│ ├── api/
│ │ ├── generated/ # 生成的 API 文件
│ │ │ ├── api_service.dart
│ │ │ ├── user_api.dart
│ │ │ └── order_api.dart
│ │ ├── models/ # 生成的模型文件
│ │ │ ├── user.dart
│ │ │ ├── order.dart
│ │ │ └── index.dart
│ │ ├── config/ # API 配置
│ │ │ ├── api_config.dart
│ │ │ └── dio_config.dart
│ │ └── interceptors/ # 拦截器
│ │ ├── auth_interceptor.dart
│ │ └── error_interceptor.dart
│ ├── services/ # 业务服务层
│ └── ...
├── swagger.json # OpenAPI 文档
└── generator_config.yaml # 生成器配置
```
### 3. 配置文件
创建 `generator_config.yaml`
```yaml
# 生成器配置
generator:
className: "ApiService"
outputDir: "lib/api/generated"
generateModularApis: true
generateBaseResult: true
generatePagination: true
generateFileUpload: true
# 类型映射
typeMapping:
"integer": "int"
"number": "double"
"boolean": "bool"
# 排除的标签
excludeTags:
- "Internal"
- "Debug"
# 自定义模板
templates:
baseResultType: "ApiResult"
pageResultType: "PagedResult"
```
---
## 📝 最佳实践
### 1. OpenAPI 文档规范
#### ✅ 推荐的文档结构
```json
{
"openapi": "3.0.1",
"info": {
"title": "Your API",
"version": "v1",
"description": "API description"
},
"servers": [
{
"url": "https://api.yourdomain.com",
"description": "Production server"
}
],
"components": {
"schemas": {
"BaseResult": {
"type": "object",
"properties": {
"success": {"type": "boolean"},
"message": {"type": "string"},
"data": {"type": "object"}
}
},
"User": {
"type": "object",
"required": ["id", "username"],
"properties": {
"id": {"type": "integer"},
"username": {"type": "string"},
"email": {"type": "string", "nullable": true}
}
}
}
}
}
```
#### ❌ 避免的常见问题
```json
// 不要:缺少 schema 定义
{
"responses": {
"200": {
"description": "Success"
// 缺少 content 和 schema
}
}
}
// 不要:模糊的类型定义
{
"properties": {
"data": {"type": "object"} // 应该明确定义结构
}
}
// 不要:不一致的命名
{
"paths": {
"/getUsers": {}, // 应该用 RESTful 风格
"/user/create": {} // 应该是 POST /users
}
}
```
### 2. 代码生成最佳实践
#### 选择合适的生成器
```dart
// 小型项目 - 基础生成器
final generator = RetrofitApiGenerator(
className: 'ApiService',
splitByTags: true,
);
// 中型项目 - 优化生成器
final generator = OptimizedRetrofitGenerator(
className: 'ApiService',
generateModularApis: true,
generateBaseResult: true,
generatePagination: true,
);
// 大型项目 - 性能生成器
final generator = PerformanceGenerator(
maxConcurrency: 8,
enableCaching: true,
enableIncremental: true,
);
```
#### 配置合适的解析器
```dart
// 开发环境 - 详细统计
final parser = PerformanceParser(
config: ParseConfig(
enablePerformanceStats: true,
enableDebugMode: true,
logLevel: LogLevel.debug,
),
);
// 生产环境 - 高性能
final parser = PerformanceParser(
config: ParseConfig(
enableParallelParsing: true,
enableCaching: true,
maxConcurrency: 4,
enableMemoryOptimization: true,
),
);
```
### 3. 错误处理策略
#### 分层错误处理
```dart
class ApiService {
final Dio _dio;
ApiService(this._dio) {
_setupInterceptors();
}
void _setupInterceptors() {
// 请求拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 添加认证头
options.headers['Authorization'] = 'Bearer $token';
handler.next(options);
},
onError: (error, handler) {
// 统一错误处理
final apiError = _handleError(error);
handler.reject(DioException(
requestOptions: error.requestOptions,
error: apiError,
));
},
));
}
ApiError _handleError(DioException error) {
switch (error.response?.statusCode) {
case 401:
return ApiError.unauthorized();
case 403:
return ApiError.forbidden();
case 404:
return ApiError.notFound();
case 500:
return ApiError.serverError();
default:
return ApiError.unknown(error.message);
}
}
}
```
#### 业务层错误处理
```dart
class UserService {
final UserApi _userApi;
UserService(this._userApi);
Future<Result<User>> getUser(int userId) async {
try {
final response = await _userApi.getUser(userId);
if (response.success) {
return Result.success(response.data!);
} else {
return Result.failure(response.message ?? 'Unknown error');
}
} on ApiError catch (e) {
return Result.failure(e.message);
} catch (e) {
return Result.failure('Network error: $e');
}
}
}
```
### 4. 性能优化
#### 缓存策略
```dart
// 配置智能缓存
final cache = SmartCache<String>(
maxSize: 1000,
strategy: CacheStrategy.smart,
defaultTtl: Duration(minutes: 30),
);
// 使用缓存包装 API 调用
class CachedApiService {
final ApiService _apiService;
final SmartCache<String> _cache;
Future<BaseResult<User>> getUser(int userId) async {
final cacheKey = 'user_$userId';
// 尝试从缓存获取
final cached = _cache.get(cacheKey);
if (cached != null) {
return BaseResult.fromJson(jsonDecode(cached));
}
// 从 API 获取
final result = await _apiService.getUser(userId);
// 缓存结果
if (result.success) {
_cache.put(cacheKey, jsonEncode(result.toJson()));
}
return result;
}
}
```
#### 并发控制
```dart
class ApiService {
final Semaphore _semaphore = Semaphore(3); // 限制并发数
Future<T> _executeWithLimit<T>(Future<T> Function() operation) async {
await _semaphore.acquire();
try {
return await operation();
} finally {
_semaphore.release();
}
}
Future<List<User>> getUsers(List<int> userIds) async {
final futures = userIds.map((id) =>
_executeWithLimit(() => getUser(id))
);
return Future.wait(futures);
}
}
```
---
## 🔧 高级配置
### 1. 自定义生成器
```dart
class CustomRetrofitGenerator extends BaseGenerator {
@override
String generate() {
final buffer = StringBuffer();
// 自定义文件头
buffer.writeln('// Custom Generated API');
buffer.writeln('// Generated at: ${DateTime.now()}');
// 自定义导入
buffer.writeln("import 'package:dio/dio.dart';");
buffer.writeln("import 'package:retrofit/retrofit.dart';");
// 生成自定义代码
_generateCustomMethods(buffer);
return buffer.toString();
}
void _generateCustomMethods(StringBuffer buffer) {
// 实现自定义生成逻辑
}
}
```
### 2. 自定义验证规则
```dart
class CustomValidationRule extends ValidationRule {
@override
String get name => 'CustomRule';
@override
List<ValidationError> validate(SwaggerDocument document) {
final errors = <ValidationError>[];
// 检查所有 API 是否有描述
for (final path in document.paths.values) {
for (final operation in path.operations.values) {
if (operation.summary?.isEmpty ?? true) {
errors.add(ValidationError(
severity: ErrorSeverity.warning,
title: 'Missing API description',
description: 'API ${operation.operationId} lacks description',
location: operation.operationId,
));
}
}
}
return errors;
}
}
```
### 3. 自定义模板
创建自定义模板文件 `templates/custom_api.mustache`
```mustache
/// {{title}} API
/// Generated by Custom Generator
class {{className}} {
final Dio _dio;
{{className}}(this._dio);
{{#operations}}
/// {{summary}}
@{{httpMethod}}('{{path}}')
Future<{{returnType}}> {{methodName}}(
{{#parameters}}
@{{paramType}}('{{name}}') {{type}} {{name}},
{{/parameters}}
);
{{/operations}}
}
```
使用自定义模板:
```dart
final generator = OptimizedRetrofitGenerator(
templatePath: 'templates/custom_api.mustache',
customVariables: {
'author': 'Your Name',
'generatedAt': DateTime.now().toIso8601String(),
},
);
```
---
## 🐛 故障排除
### 常见问题及解决方案
#### 1. 解析失败
**问题**: `SwaggerParseException: Invalid JSON format`
**解决方案**:
```bash
# 验证 JSON 格式
jsonlint swagger.json
# 或使用在线工具验证
# https://jsonlint.com/
```
**问题**: `Reference not found: #/components/schemas/User`
**解决方案**:
```dart
// 检查引用是否存在
final validator = EnhancedValidator();
final isValid = validator.validateDocument(document);
final errors = validator.errorReporter.getErrorsBySeverity(ErrorSeverity.error);
for (final error in errors) {
if (error.title.contains('Reference')) {
print('缺少引用: ${error.description}');
}
}
```
#### 2. 生成错误
**问题**: 生成的代码包含语法错误
**解决方案**:
```dart
// 启用严格验证
final validator = EnhancedValidator(
strictMode: true,
customRules: [
TypeConsistencyRule(),
NamingConventionRule(),
],
);
// 检查生成前的文档质量
if (!validator.validateDocument(document)) {
final report = validator.errorReporter.generateReport();
print('文档质量问题:\n$report');
return;
}
```
**问题**: 生成的类型不存在
**解决方案**:
```dart
// 检查 Schema 定义
final schemas = document.components?.schemas ?? {};
if (!schemas.containsKey('YourType')) {
print('错误: Schema YourType 不存在');
print('可用 Schemas: ${schemas.keys.join(', ')}');
}
```
#### 3. 性能问题
**问题**: 解析大文档时内存不足
**解决方案**:
```dart
// 启用内存优化
final parser = PerformanceParser(
config: ParseConfig(
enableMemoryOptimization: true,
enableStreamParsing: true,
maxConcurrency: 2, // 降低并发数
),
);
```
**问题**: 生成速度慢
**解决方案**:
```dart
// 启用并行生成和缓存
final generator = PerformanceGenerator(
maxConcurrency: 4,
enableCaching: true,
enableIncremental: true,
cacheStrategy: CacheStrategy.smart,
);
```
### 调试技巧
#### 1. 启用详细日志
```dart
// 配置日志级别
import 'package:logging/logging.dart';
void setupLogging() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
}
// 在解析器中使用
final parser = PerformanceParser(
config: ParseConfig(
enableVerboseLogging: true,
logLevel: LogLevel.debug,
),
);
```
#### 2. 性能分析
```dart
// 使用性能监控器
final monitor = PerformanceMonitor();
monitor.startOperation('full_generation');
// 解析阶段
monitor.startOperation('parse');
final document = await parser.parseDocument(jsonString);
monitor.endOperation('parse');
// 验证阶段
monitor.startOperation('validate');
final isValid = validator.validateDocument(document);
monitor.endOperation('validate');
// 生成阶段
monitor.startOperation('generate');
final code = generator.generateFromDocument(document);
monitor.endOperation('generate');
monitor.endOperation('full_generation');
// 输出性能报告
final report = monitor.generateReport();
print(report);
```
#### 3. 内存分析
```dart
// 监控内存使用
import 'dart:developer';
void trackMemoryUsage(String operation) {
final info = ProcessInfo.currentRss;
print('$operation - Memory: ${info / 1024 / 1024:.2f}MB');
}
trackMemoryUsage('Before parsing');
final document = await parser.parseDocument(jsonString);
trackMemoryUsage('After parsing');
final code = generator.generateFromDocument(document);
trackMemoryUsage('After generation');
```
---
## 📚 示例项目
### 完整示例项目结构
```
example_project/
├── lib/
│ ├── main.dart
│ ├── api/
│ │ ├── generated/
│ │ │ ├── api_service.dart
│ │ │ ├── user_api.dart
│ │ │ └── order_api.dart
│ │ ├── models/
│ │ │ ├── user.dart
│ │ │ ├── order.dart
│ │ │ └── index.dart
│ │ ├── config/
│ │ │ └── api_config.dart
│ │ └── services/
│ │ ├── user_service.dart
│ │ └── order_service.dart
│ ├── ui/
│ │ ├── pages/
│ │ └── widgets/
│ └── utils/
├── swagger.json
├── generator_config.yaml
└── generate_api.dart
```
### 生成脚本示例
`generate_api.dart`:
```dart
import 'dart:io';
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart';
Future<void> main() async {
print('🚀 开始生成 API 代码...');
try {
// 1. 配置和初始化
final config = await _loadConfig();
final parser = _createParser(config);
final validator = _createValidator(config);
final generator = _createGenerator(config);
// 2. 解析文档
print('📖 解析 OpenAPI 文档...');
final document = await _parseDocument(parser, config.swaggerPath);
// 3. 验证文档
print('✅ 验证文档...');
await _validateDocument(validator, document);
// 4. 生成代码
print('🔧 生成代码...');
await _generateCode(generator, document, config);
// 5. 后处理
print('🎯 后处理...');
await _postProcess(config);
print('✅ API 代码生成完成!');
} catch (e, stackTrace) {
print('❌ 生成失败: $e');
print('堆栈跟踪: $stackTrace');
exit(1);
}
}
// 实现各个辅助方法...
```
---
**文档版本**: v2.0
**最后更新**: 2025-01-24
**维护者**: Max

View File

@ -1,55 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# VS Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# Generated files
*.g.dart
*.freezed.dart
*.gr.dart
# Generated API code (可选:如果团队统一生成则忽略,否则提交)
# lib/generated/
# Build files
build/
.dart_tool/

View File

@ -1,70 +0,0 @@
.PHONY: help install generate build clean run test
# 默认目标
help:
@echo "可用命令:"
@echo " make install - 安装依赖"
@echo " make generate - 生成 API 代码"
@echo " make build - 运行 build_runner"
@echo " make clean - 清理生成的文件"
@echo " make run - 运行应用"
@echo " make test - 运行测试"
# 安装依赖
install:
@echo "📦 安装依赖..."
@flutter pub get
@echo "✅ 依赖安装完成"
# 生成 API 代码
generate:
@echo "🚀 生成 API 代码..."
@dart run swagger_generator_flutter generate --all
@echo "✅ API 代码生成完成"
# 运行 build_runner
build: generate
@echo "🔧 运行 build_runner..."
@dart run build_runner build --delete-conflicting-outputs
@dart format lib/generated
@echo "✅ 构建完成"
# 监听模式
watch:
@echo "👀 启动监听模式..."
@dart run build_runner watch --delete-conflicting-outputs
# 清理生成的文件
clean:
@echo "🧹 清理生成的文件..."
@rm -rf lib/generated
@flutter clean
@echo "✅ 清理完成"
# 重新生成
regenerate: clean build
# 运行应用
run:
@echo "🚀 运行应用..."
@flutter run
# 运行测试
test:
@echo "🧪 运行测试..."
@flutter test
# 分析代码
analyze:
@echo "🔍 分析代码..."
@dart analyze
# 格式化代码
format:
@echo "📐 格式化代码..."
@dart format lib/
# 检查代码质量
check: analyze test
@echo "✅ 代码质量检查完成"

View File

@ -1,202 +0,0 @@
# 🚀 快速开始指南
5 分钟快速体验 `swagger_generator_flutter` 作为 dev_dependencies 的使用!
## ⚡ 极速开始3 步)
### 第 1 步:安装依赖
```bash
cd example/as_dev_dependency
flutter pub get
```
### 第 2 步:生成代码
**macOS/Linux:**
```bash
./generate_api.sh
```
**Windows:**
```cmd
generate_api.bat
```
**或使用 Make:**
```bash
make build
```
### 第 3 步:运行应用
```bash
flutter run
# 或
make run
```
## 📱 体验完整流程
1. **查看配置文件**
```bash
cat generator_config.yaml
```
这是代码生成器的配置,定义了输入源和输出目录。
2. **查看生成的代码**
```bash
ls -la lib/generated/api/
ls -la lib/generated/api_models/
```
3. **在代码中使用**
打开 `lib/main.dart`,取消注释以下行:
```dart
import 'generated/api/api_client.dart';
```
然后使用生成的 API
```dart
final dio = Dio();
final apiClient = ApiClient(dio);
// 调用 API
final response = await apiClient.petApi.getPetById(petId: 1);
```
## 🎯 理解项目结构
```
example/as_dev_dependency/
├── pubspec.yaml ← 定义依赖
├── generator_config.yaml ← 配置生成器
├── generate_api.sh ← 生成脚本
├── lib/
│ ├── common/ ← 基础类型定义
│ │ ├── api_response.dart
│ │ └── paged_response.dart
│ ├── generated/ ← 生成的代码(自动)
│ │ ├── api/
│ │ └── api_models/
│ └── main.dart ← 应用入口
└── README.md ← 详细文档
```
## 💡 核心文件说明
### 1. `pubspec.yaml` - 依赖配置
```yaml
dev_dependencies:
swagger_generator_flutter:
path: ../../ # 示例使用相对路径
```
在实际项目中使用:
```yaml
dev_dependencies:
swagger_generator_flutter:
git:
url: https://github.com/your-org/swagger_generator_flutter.git
ref: main
```
### 2. `generator_config.yaml` - 生成器配置
关键配置项:
```yaml
input:
swagger_url: "https://petstore3.swagger.io/api/v3/openapi.json"
output:
base_dir: "./lib/generated"
generation:
api:
enabled: true
use_retrofit: true
models:
enabled: true
use_json_serializable: true
```
### 3. `generate_api.sh` - 自动化脚本
执行流程:
1. ✅ 生成 API 代码
2. ✅ 运行 build_runner
3. ✅ 格式化代码
4. ✅ 分析代码
## 🔄 工作流程
```mermaid
graph LR
A[修改配置] --> B[运行生成脚本]
B --> C[生成 API 代码]
C --> D[运行 build_runner]
D --> E[格式化代码]
E --> F[在项目中使用]
```
## 📋 常用命令速查
| 命令 | 说明 |
|------|------|
| `make install` | 安装依赖 |
| `make generate` | 生成 API 代码 |
| `make build` | 生成并构建 |
| `make watch` | 监听模式 |
| `make clean` | 清理生成的文件 |
| `make run` | 运行应用 |
## 🎓 学习路径
1. ✅ **快速开始**(当前文档)
2. 📖 [完整 README](README.md) - 详细项目说明
3. 📚 [使用指南](../../USAGE_AS_DEV_DEPENDENCY.md) - 深入教程
4. 🔧 [配置模板](../../generator_config.template.yaml) - 配置参考
5. 📖 [项目文档](../../README.md) - 完整项目文档
## ❓ 遇到问题?
**生成失败?**
```bash
# 检查配置文件
cat generator_config.yaml
# 重新安装依赖
flutter pub get
# 清理后重新生成
make clean
make build
```
**运行错误?**
```bash
# 检查是否已生成代码
ls lib/generated/
# 检查是否运行了 build_runner
ls lib/generated/**/*.g.dart
```
**更多帮助?**
- 查看 [README.md](README.md)
- 查看 [故障排除](README.md#-故障排除)
- 提交 Issue
## 🎉 下一步
现在您已经了解了基础用法,可以:
1. 🔧 修改 `generator_config.yaml` 配置
2. 📝 连接您自己的 Swagger API
3. 🚀 在实际项目中集成
4. 📚 阅读完整文档了解高级功能
祝编码愉快! 🎊

View File

@ -1,330 +0,0 @@
# Swagger Generator Flutter - 示例应用
这是一个完整的示例应用,展示如何在 Flutter 项目中使用 `swagger_generator_flutter` 作为 `dev_dependencies` 来生成 API 代码。
## 📋 项目说明
本示例应用使用 [Petstore API](https://petstore3.swagger.io) 作为演示,展示完整的代码生成流程。
## 🚀 快速开始
### 1. 安装依赖
```bash
# 使用 flutter 命令
flutter pub get
# 或使用 Makefile
make install
```
### 2. 生成 API 代码
#### 方式 1: 使用脚本(推荐)
**macOS/Linux:**
```bash
chmod +x generate_api.sh
./generate_api.sh
```
**Windows:**
```cmd
generate_api.bat
```
#### 方式 2: 使用 Makefile
```bash
# 生成并构建
make build
# 或分步执行
make generate # 生成 API 代码
make build # 运行 build_runner
```
#### 方式 3: 手动执行命令
```bash
# 1. 生成 API 代码
dart run swagger_generator_flutter generate --all
# 2. 生成序列化代码
dart run build_runner build --delete-conflicting-outputs
# 3. 格式化代码
dart format lib/generated
```
### 3. 运行应用
```bash
# 使用 flutter 命令
flutter run
# 或使用 Makefile
make run
```
## 📁 项目结构
```
example_app/
├── lib/
│ ├── common/ # 公共类
│ │ ├── api_response.dart # 通用响应包装
│ │ └── paged_response.dart # 分页响应包装
│ ├── generated/ # 生成的代码(自动生成)
│ │ ├── api/
│ │ │ ├── v1/
│ │ │ │ ├── pet_api.dart
│ │ │ │ ├── store_api.dart
│ │ │ │ ├── user_api.dart
│ │ │ │ └── index.dart
│ │ │ └── api_client.dart
│ │ ├── api_models/
│ │ │ ├── request/
│ │ │ ├── result/
│ │ │ ├── parameters/
│ │ │ ├── enums/
│ │ │ └── index.dart
│ │ ├── api_paths.dart
│ │ ├── api_documentation.md
│ │ └── SUMMARY.md
│ └── main.dart # 应用入口
├── generator_config.yaml # 代码生成器配置
├── pubspec.yaml # 项目依赖配置
├── generate_api.sh # 生成脚本macOS/Linux
├── generate_api.bat # 生成脚本Windows
├── Makefile # Make 命令配置
└── README.md # 本文件
```
## 🔧 配置说明
### pubspec.yaml
```yaml
dev_dependencies:
# 使用相对路径引用(示例)
swagger_generator_flutter:
path: ../../
# 实际项目中使用 git 引用
# swagger_generator_flutter:
# git:
# url: https://github.com/your-org/swagger_generator_flutter.git
# ref: main
```
### generator_config.yaml
主要配置项:
- `input.swagger_url`: Swagger 文档 URL
- `output.base_dir`: 代码输出目录
- `generation.api.base_result_type`: 基础响应类型
- `generation.models.use_json_serializable`: 使用 JSON 序列化
完整配置请参考项目中的 `generator_config.yaml` 文件。
## 💻 使用生成的 API
### 1. 创建 API 客户端
```dart
import 'package:dio/dio.dart';
import 'generated/api/api_client.dart';
final dio = Dio(BaseOptions(
baseUrl: 'https://petstore3.swagger.io/api/v3',
connectTimeout: const Duration(seconds: 30),
));
final apiClient = ApiClient(dio);
```
### 2. 调用 API
```dart
// 获取宠物信息
final petApi = apiClient.petApi;
final response = await petApi.getPetById(petId: 1);
if (response.isSuccess) {
print('Pet: ${response.data}');
}
// 获取用户信息
final userApi = apiClient.userApi;
final user = await userApi.getUserByName(username: 'john');
```
### 3. 使用生成的模型
```dart
import 'generated/api_models/index.dart';
// 创建请求对象
final createPetRequest = CreatePetRequest(
name: 'Fluffy',
category: 'Cat',
status: PetStatus.available,
);
// 调用 API
final response = await petApi.createPet(request: createPetRequest);
```
## 🛠️ Makefile 命令
本项目提供了便捷的 Makefile 命令:
| 命令 | 说明 |
|------|------|
| `make help` | 显示帮助信息 |
| `make install` | 安装依赖 |
| `make generate` | 生成 API 代码 |
| `make build` | 生成并构建(包含 build_runner |
| `make watch` | 监听模式(自动重新构建) |
| `make clean` | 清理生成的文件 |
| `make regenerate` | 清理并重新生成 |
| `make run` | 运行应用 |
| `make test` | 运行测试 |
| `make analyze` | 分析代码 |
| `make format` | 格式化代码 |
| `make check` | 完整代码质量检查 |
## 🔄 工作流程
### 开发流程
1. 修改 `generator_config.yaml` 配置(如果需要)
2. 运行 `make build` 生成代码
3. 在代码中使用生成的 API
4. 运行 `make run` 测试应用
### 更新 API 流程
1. 后端更新 Swagger 文档
2. 运行 `make regenerate` 重新生成代码
3. 检查生成的代码变更
4. 更新使用 API 的代码(如有破坏性变更)
5. 运行测试确保一切正常
## 📝 最佳实践
### 1. 版本控制
**选项 A: 忽略生成的代码**(推荐用于团队协作)
`.gitignore` 中添加:
```
lib/generated/
```
优点:
- 避免合并冲突
- 保持仓库清洁
- 团队成员各自生成
缺点:
- 需要额外的构建步骤
- CI/CD 需要生成代码
**选项 B: 提交生成的代码**
优点:
- 克隆即可运行
- CI/CD 更简单
- 明确的代码变更记录
缺点:
- 可能产生大量代码变更
- 合并冲突风险
### 2. 基础类型定义
确保项目中已定义好基础响应类型:
- `ApiResponse<T>`: 通用响应包装
- `PagedResponse<T>`: 分页响应包装
这些类型需要在生成代码前存在。
### 3. 配置管理
为不同环境创建不同的配置文件:
```
generator_config.yaml # 默认配置
generator_config.dev.yaml # 开发环境
generator_config.staging.yaml # 测试环境
generator_config.prod.yaml # 生产环境
```
### 4. CI/CD 集成
在 CI/CD 流程中添加代码生成步骤:
```yaml
# .github/workflows/build.yml
- name: Generate API code
run: |
dart run swagger_generator_flutter generate --all
dart run build_runner build --delete-conflicting-outputs
- name: Check for uncommitted changes
run: |
git diff --exit-code || (echo "Generated code has changes" && exit 1)
```
## 🐛 故障排除
### 问题 1: 找不到命令
```
Error: Could not find package swagger_generator_flutter
```
**解决**:
```bash
flutter pub get
```
### 问题 2: build_runner 失败
```
Error: A value of type 'Null' can't be returned from the method 'fromJson'
```
**解决**: 检查 `api_response.dart``paged_response.dart` 是否正确生成了 `.g.dart` 文件。
### 问题 3: 导入错误
```
Error: Not found: 'package:example_app/generated/api/api_client.dart'
```
**解决**: 确保已运行代码生成命令并且没有错误。
## 📚 相关资源
- [完整使用指南](../../USAGE_AS_DEV_DEPENDENCY.md)
- [配置文件模板](../../generator_config.template.yaml)
- [项目主文档](../../README.md)
- [Petstore API 文档](https://petstore3.swagger.io)
## 📧 获取帮助
如果遇到问题:
1. 查看 [故障排除](#-故障排除) 部分
2. 查看 [完整文档](../../USAGE_AS_DEV_DEPENDENCY.md)
3. 提交 Issue
4. 查看示例代码
## 📄 许可证
本示例应用仅用于演示目的。

View File

@ -1,23 +0,0 @@
include: package:flutter_lints/flutter.yaml
linter:
rules:
- always_declare_return_types
- always_put_required_named_parameters_first
- avoid_print
- avoid_unnecessary_containers
- prefer_const_constructors
- prefer_const_literals_to_create_immutables
- prefer_final_fields
- prefer_single_quotes
- sort_child_properties_last
- use_key_in_widget_constructors
analyzer:
exclude:
- '**/*.g.dart'
- '**/*.freezed.dart'
- 'lib/generated/**'
errors:
invalid_annotation_target: ignore

View File

@ -1,70 +0,0 @@
@echo off
REM Swagger API 代码生成脚本Windows
REM 用于示例项目的 API 代码生成
setlocal enabledelayedexpansion
echo ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
echo 🚀 Swagger API 代码生成器
echo ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
echo.
REM 步骤 1: 生成 API 代码
echo 📝 步骤 1/4: 生成 API 代码...
dart run swagger_generator_flutter generate --all
if !errorlevel! neq 0 (
echo ❌ API 代码生成失败!
exit /b 1
)
echo ✅ API 代码生成成功
echo.
REM 步骤 2: 运行 build_runner
echo 🔧 步骤 2/4: 运行 build_runner...
dart run build_runner build --delete-conflicting-outputs
if !errorlevel! neq 0 (
echo ⚠️ build_runner 执行失败(如果是首次运行,可能需要先修复基础类型)
echo 请检查 lib/common/api_response.dart 和 paged_response.dart
) else (
echo ✅ build_runner 执行成功
)
echo.
REM 步骤 3: 格式化代码
echo 📐 步骤 3/4: 格式化代码...
dart format lib/generated
echo ✅ 代码格式化完成
echo.
REM 步骤 4: 分析代码
echo 🔍 步骤 4/4: 分析代码...
dart analyze lib/generated --fatal-infos
if !errorlevel! neq 0 (
echo ⚠️ 代码分析发现问题,请检查
) else (
echo ✅ 代码分析通过
)
echo.
echo ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
echo ✨ 代码生成完成!
echo ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
echo.
echo 📂 生成的文件位置:
echo lib/generated/api/
echo lib/generated/api_models/
echo.
echo 📚 下一步:
echo 1. 查看生成的代码lib/generated/
echo 2. 在 main.dart 中取消注释 import 语句
echo 3. 运行应用flutter run
echo.
pause

View File

@ -1,69 +0,0 @@
#!/bin/bash
# Swagger API 代码生成脚本
# 用于示例项目的 API 代码生成
set -e # 遇到错误立即退出
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 Swagger API 代码生成器"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 步骤 1: 生成 API 代码
echo "📝 步骤 1/4: 生成 API 代码..."
dart run swagger_generator_flutter generate --all
if [ $? -ne 0 ]; then
echo "❌ API 代码生成失败!"
exit 1
fi
echo "✅ API 代码生成成功"
echo ""
# 步骤 2: 运行 build_runner
echo "🔧 步骤 2/4: 运行 build_runner..."
dart run build_runner build --delete-conflicting-outputs
if [ $? -ne 0 ]; then
echo "⚠️ build_runner 执行失败(如果是首次运行,可能需要先修复基础类型)"
echo " 请检查 lib/common/api_response.dart 和 paged_response.dart"
else
echo "✅ build_runner 执行成功"
fi
echo ""
# 步骤 3: 格式化代码
echo "📐 步骤 3/4: 格式化代码..."
dart format lib/generated
echo "✅ 代码格式化完成"
echo ""
# 步骤 4: 分析代码
echo "🔍 步骤 4/4: 分析代码..."
dart analyze lib/generated --fatal-infos
if [ $? -ne 0 ]; then
echo "⚠️ 代码分析发现问题,请检查"
else
echo "✅ 代码分析通过"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✨ 代码生成完成!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📂 生成的文件位置:"
echo " lib/generated/api/"
echo " lib/generated/api_models/"
echo ""
echo "📚 下一步:"
echo " 1. 查看生成的代码lib/generated/"
echo " 2. 在 main.dart 中取消注释 import 语句"
echo " 3. 运行应用flutter run"
echo ""

View File

@ -1,199 +0,0 @@
# Swagger 代码生成器配置文件
# 示例项目配置
# 基本配置
generator:
name: "xy_swagger_generator"
version: "1.0"
author: "max"
copyright: "Copyright (C) 2025 YuanXuan. All rights reserved."
# 输入配置
input:
# Swagger 文档源(支持多版本)
swagger_urls: # 完整形式:可以控制每个版本的启用状态
- url: "https://quanxue-test-api.w.23544.com:8843/swagger/v1/swagger.json"
enabled: true
- url: "https://quanxue-test-api.w.23544.com:8843/swagger/v2/swagger.json"
enabled: true
# 验证配置
validate_schema: true
strict_mode: false
# 输出配置
output:
# 输出目录
base_dir: "./lib/generated"
api_dir: "./lib/generated/api"
models_dir: "./lib/generated/api_models"
# 文件命名
api_file_suffix: "_api.dart"
model_file_suffix: ".dart"
# 是否按 tag 分组
split_by_tags: true
# 跳过的目录列表(这些目录下的文件将不会被生成)
# 支持相对路径和绝对路径,支持目录名或完整路径
ignored_directories:
# - "api/v1" # 跳过 v1 版本的 API
# - "api_models/request" # 跳过请求模型目录
# - "./lib/generated/api/v2" # 跳过特定路径
# 跳过的文件名列表(这些文件将不会被生成)
# 支持精确匹配、通配符匹配和模式匹配
ignored_files:
# 精确匹配文件名
# - "user_api.dart" # 跳过名为 user_api.dart 的文件
# - "mobile_manager_api.dart" # 跳过指定文件
# 通配符匹配(支持前缀和后缀)
# - "*_api.dart" # 跳过所有以 _api.dart 结尾的文件
# - "user*.dart" # 跳过所有以 user 开头的 .dart 文件
# - "*manager*" # 跳过所有包含 manager 的文件名
# 示例:跳过所有 v1 版本的 API 文件(如果文件名包含版本信息)
# - "*_api_v1.dart"
# - "*V1*.dart"
# 代码生成配置
generation:
# API 接口配置
api:
enabled: true
use_retrofit: true
use_dio: true
parser: "JsonSerializable"
# 版本提取配置(多版本支持)
version_extraction:
# 版本提取正则表达式模式
pattern: "/api/v(\\d+)/"
# 默认版本(当无法从路径提取版本时使用)
default_version: "v1"
# 基础类型配置
base_result_type: "ApiResponse"
base_page_result_type: "PagedResponse"
base_result_import: "package:example_app/common/api_response.dart"
base_page_result_import: "package:example_app/common/paged_response.dart"
# 方法命名
method_naming: "camelCase"
# 数据模型配置
models:
enabled: true
use_json_serializable: true
# JsonSerializable 配置
json_serializable:
checked: true
include_if_null: false
explicit_to_json: true
# 类命名
class_naming: "PascalCase"
field_naming: "camelCase"
# 构造函数配置
use_const_constructor: true
required_for_non_nullable: true
# 类型映射配置
type_mapping:
string: "String"
integer: "int"
number: "double"
boolean: "bool"
array: "List"
object: "Map<String, dynamic>"
date: "DateTime"
date-time: "DateTime"
binary: "Uint8List"
# 导入管理配置
imports:
on_demand: true
auto_sort: true
group_imports: true
dart_imports:
- "dart:convert"
- "dart:typed_data"
package_imports:
- "package:dio/dio.dart"
- "package:retrofit/retrofit.dart"
- "package:json_annotation/json_annotation.dart"
# 验证配置
validation:
strict_mode: false
checks:
- "schema_exists"
- "ref_resolution"
- "type_consistency"
on_error: "warn"
on_warning: "log"
# 优化配置
optimization:
remove_unused_imports: true
optimize_imports: true
cache_schemas: true
parallel_generation: false
lazy_loading: true
# 调试配置
debug:
verbose: false
debug_output: false
performance_monitoring: false
generation_stats: true
# 模板配置
templates:
# 文件头模板
# 支持模板变量:
# {fileName} - 文件名(如 "user_api.dart"
# {fileType} - 文件类型描述(如 "API 接口定义"、"模型定义"
# {swaggerUrl} - Swagger 文档 URL
# {generatorName} - 生成器名称(从 generator.name 读取)
# {author} - 作者(从 generator.author 读取)
# {copyright} - 版权信息(从 generator.copyright 读取)
file_header: |
// {fileType}
// 基于 Swagger API 文档: {swaggerUrl}
// 由 {generatorName} by {author} 生成
// {copyright}
# API 类模板
# 支持模板变量:
# {tagName} - Tag 名称(如 "User"、"Task"
# {className} - 类名(如 "UserApi"、"TaskApi"
api_class: |
/// {tagName} API 接口
/// 负责处理 {tagName} 相关的接口
@RestApi(parser: Parser.JsonSerializable)
abstract class {className} {
factory {className}(Dio dio, {String? baseUrl}) = _{className};
}
# 模型类模板
# 支持模板变量:
# {className} - 类名(如 "User"、"Task"
# {constructorParams} - 构造函数参数列表
model_class: |
@JsonSerializable(checked: true, includeIfNull: false)
class {className} {
const {className}({constructorParams});
factory {className}.fromJson(Map<String, dynamic> json) =>
_${className}FromJson(json);
Map<String, dynamic> toJson() => _${className}ToJson(this);
}

View File

@ -1,636 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
url: "https://pub.flutter-io.cn"
source: hosted
version: "67.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.4.1"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.13.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.0"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.3.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.12.0"
characters:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.4"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.11.0"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.2"
crypto:
dependency: transitive
description:
name: crypto
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.7"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.6"
dio:
dependency: "direct main"
description:
name: dio
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.9.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.3"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
http:
dependency: transitive
description:
name: http
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.2"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.9.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.8.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
logger:
dependency: "direct main"
description:
name: logger
sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.2"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.2"
protobuf:
dependency: transitive
description:
name: protobuf
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.0"
retrofit:
dependency: "direct main"
description:
name: retrofit
sha256: "7d78824afa6eeeaf6ac58220910ee7a97597b39e93360d4bda230b7c6df45089"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.9.0"
retrofit_generator:
dependency: "direct dev"
description:
name: retrofit_generator
sha256: "8dfc406cdfa171f33cbd21bf5bd8b6763548cc217de19cdeaa07a76727fac4ca"
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.2.1"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.5"
source_span:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
swagger_generator_flutter:
dependency: "direct dev"
description:
path: "../.."
relative: true
source: path
version: "2.1.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.6"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.2"
tuple:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.flutter-io.cn"
source: hosted
version: "15.0.2"
watcher:
dependency: transitive
description:
name: watcher
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.4"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.3"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@ -1,41 +0,0 @@
name: example_app
description: Example Flutter app using swagger_generator_flutter as dev_dependency
version: 1.0.0
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
# HTTP 客户端
dio: ^5.0.0
retrofit: ^4.0.0
# JSON 序列化
json_annotation: ^4.8.0
# 其他依赖
logger: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
# Swagger 代码生成器(使用本地路径作为示例)
swagger_generator_flutter:
path: ../../
# 代码生成工具
build_runner: ^2.4.7
retrofit_generator: ^8.0.0
json_serializable: ^6.7.1
# 代码分析
flutter_lints: ^3.0.0
flutter:
uses-material-design: true

View File

@ -1,107 +0,0 @@
#!/bin/bash
# 测试示例项目脚本
# 用于快速验证 dev_dependencies 功能是否正常工作
set -e
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🧪 测试 Swagger Generator 示例项目"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 检查当前目录
if [ ! -f "pubspec.yaml" ]; then
echo "❌ 错误:请在示例项目目录中运行此脚本"
echo " cd example/as_dev_dependency"
exit 1
fi
echo "📂 当前目录: $(pwd)"
echo ""
# 步骤 1: 清理
echo "🧹 步骤 1/5: 清理旧文件..."
rm -rf lib/generated .dart_tool build
echo "✅ 清理完成"
echo ""
# 步骤 2: 安装依赖
echo "📦 步骤 2/5: 安装依赖..."
flutter pub get
if [ $? -ne 0 ]; then
echo "❌ 依赖安装失败"
exit 1
fi
echo "✅ 依赖安装成功"
echo ""
# 步骤 3: 生成 API 代码
echo "🚀 步骤 3/5: 生成 API 代码..."
dart run swagger_generator_flutter generate --all
if [ $? -ne 0 ]; then
echo "❌ API 代码生成失败"
exit 1
fi
echo "✅ API 代码生成成功"
echo ""
# 步骤 4: 检查生成的文件
echo "🔍 步骤 4/5: 检查生成的文件..."
if [ ! -d "lib/generated" ]; then
echo "❌ 生成目录不存在"
exit 1
fi
if [ ! -d "lib/generated/api" ]; then
echo "❌ API 目录不存在"
exit 1
fi
if [ ! -d "lib/generated/api_models" ]; then
echo "❌ Models 目录不存在"
exit 1
fi
# 统计生成的文件
api_files=$(find lib/generated/api -name "*.dart" | wc -l | tr -d ' ')
model_files=$(find lib/generated/api_models -name "*.dart" | wc -l | tr -d ' ')
echo " 📄 生成的 API 文件: $api_files"
echo " 📄 生成的 Model 文件: $model_files"
echo "✅ 文件检查通过"
echo ""
# 步骤 5: 分析代码
echo "🔬 步骤 5/5: 分析生成的代码..."
dart analyze lib/generated 2>&1 | head -20
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✨ 测试完成!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📊 统计信息:"
echo " ✅ API 文件: $api_files"
echo " ✅ Model 文件: $model_files"
echo ""
echo "📁 生成的文件位置:"
echo " lib/generated/api/"
echo " lib/generated/api_models/"
echo ""
echo "🎯 下一步:"
echo " 1. 运行 build_runner:"
echo " dart run build_runner build --delete-conflicting-outputs"
echo ""
echo " 2. 运行应用:"
echo " flutter run"
echo ""
echo " 3. 查看生成的代码:"
echo " ls -la lib/generated/"
echo ""

239
example/basic_usage.dart Normal file
View File

@ -0,0 +1,239 @@
/// 使
/// 使 Swagger Generator Flutter API
library;
import 'dart:convert';
import 'dart:io';
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart';
void main() async {
print('🚀 基础使用示例');
print('=' * 50);
try {
// 1. OpenAPI
print('📖 读取 OpenAPI 文档...');
final jsonString = await File('swagger.json').readAsString();
print('✅ 文档大小: ${(jsonString.length / 1024).toStringAsFixed(2)}KB');
// 2.
print('\n🔍 解析文档...');
final document = SwaggerDocument.fromJson(jsonDecode(jsonString));
print('✅ 解析完成');
print(' - 标题: ${document.title}');
print(' - 版本: ${document.version}');
print(' - 路径数: ${document.paths.length}');
print(' - 模型数: ${document.models.length}');
// 3.
print('\n✅ 验证文档...');
final validator = EnhancedValidator(
includeWarnings: true,
);
final isValid = validator.validateDocument(document);
final errors =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.error);
final warnings =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.warning);
print(' - 验证结果: ${isValid ? "通过" : "失败"}');
print(' - 错误数: ${errors.length}');
print(' - 警告数: ${warnings.length}');
if (errors.isNotEmpty) {
print('\n❌ 发现错误:');
for (final error in errors.take(3)) {
print(' - ${error.title}: ${error.description}');
}
}
// 4. API
print('\n🔧 生成基础 API 代码...');
final generator = RetrofitApiGenerator(
className: 'BasicApiService',
splitByTags: true, // 使
useRetrofit: true,
generateModels: true,
);
final generatedCode = generator.generateFromDocument(document);
print('✅ 代码生成完成');
print(' - 代码大小: ${(generatedCode.length / 1024).toStringAsFixed(2)}KB');
print(' - 代码行数: ${generatedCode.split('\n').length}');
// 5.
print('\n💾 保存生成的代码...');
final outputDir = Directory('example/generated');
if (!outputDir.existsSync()) {
outputDir.createSync(recursive: true);
}
final outputFile = File('example/generated/basic_api_service.dart');
await outputFile.writeAsString(generatedCode);
print('✅ 代码已保存到: ${outputFile.path}');
// 6.
print('\n📄 生成的代码片段:');
print('-' * 30);
final lines = generatedCode.split('\n');
for (int i = 0; i < 20 && i < lines.length; i++) {
print('${(i + 1).toString().padLeft(2)}: ${lines[i]}');
}
if (lines.length > 20) {
print('... (还有 ${lines.length - 20} 行)');
}
print('\n🎉 基础使用示例完成!');
} catch (e, stackTrace) {
print('❌ 发生错误: $e');
print('堆栈跟踪: $stackTrace');
}
}
/// OpenAPI
Future<void> createSampleDocument() async {
final sampleDoc = {
'openapi': '3.0.3',
'info': {
'title': 'Sample API',
'version': '1.0.0',
'description': 'A sample API for demonstration',
},
'servers': [
{
'url': 'https://api.example.com',
'description': 'Production server',
},
],
'paths': {
'/users': {
'get': {
'summary': 'Get all users',
'operationId': 'getUsers',
'tags': ['users'],
'parameters': [
{
'name': 'page',
'in': 'query',
'required': false,
'schema': {'type': 'integer', 'default': 1},
'description': 'Page number',
},
],
'responses': {
'200': {
'description': 'Success',
'content': {
'application/json': {
'schema': {
'type': 'array',
'items': {
'\$ref': '#/components/schemas/User',
},
},
},
},
},
},
},
'post': {
'summary': 'Create user',
'operationId': 'createUser',
'tags': ['users'],
'requestBody': {
'required': true,
'content': {
'application/json': {
'schema': {
'\$ref': '#/components/schemas/CreateUserRequest',
},
},
},
},
'responses': {
'201': {
'description': 'User created',
'content': {
'application/json': {
'schema': {
'\$ref': '#/components/schemas/User',
},
},
},
},
},
},
},
'/users/{id}': {
'get': {
'summary': 'Get user by ID',
'operationId': 'getUserById',
'tags': ['users'],
'parameters': [
{
'name': 'id',
'in': 'path',
'required': true,
'schema': {'type': 'integer'},
'description': 'User ID',
},
],
'responses': {
'200': {
'description': 'User found',
'content': {
'application/json': {
'schema': {
'\$ref': '#/components/schemas/User',
},
},
},
},
'404': {
'description': 'User not found',
},
},
},
},
},
'components': {
'schemas': {
'User': {
'type': 'object',
'properties': {
'id': {'type': 'integer', 'format': 'int64'},
'name': {'type': 'string'},
'email': {'type': 'string', 'format': 'email'},
'createdAt': {'type': 'string', 'format': 'date-time'},
},
'required': ['id', 'name', 'email'],
},
'CreateUserRequest': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'email': {'type': 'string', 'format': 'email'},
},
'required': ['name', 'email'],
},
},
},
};
final jsonString = const JsonEncoder.withIndent(' ').convert(sampleDoc);
await File('example/sample_swagger.json').writeAsString(jsonString);
print('✅ 示例文档已创建: example/sample_swagger.json');
}
///
///
/// 使:
/// ```bash
/// dart run example/basic_usage.dart
/// ```
///
/// :
/// ```dart
/// await createSampleDocument();
/// ```

View File

@ -0,0 +1,424 @@
///
/// Flutter 使 Swagger Generator
library;
import 'dart:io';
import 'package:swagger_generator_flutter/swagger_generator_flutter.dart';
///
class ProjectConfig {
static const String projectName = 'MyFlutterApp';
static const String apiServiceName = 'ApiService';
static const String outputDir = 'lib/api/generated';
static const String swaggerFile = 'swagger.json';
// API
static const String baseUrl = 'https://api.myapp.com';
static const String apiVersion = 'v1';
//
static const bool enableModularApis = true;
static const bool enableBaseResult = true;
static const bool enablePagination = true;
static const bool enableFileUpload = true;
static const bool enableCaching = true;
static const bool enableValidation = true;
}
void main() async {
print('🚀 ${ProjectConfig.projectName} API 代码生成');
print('=' * 60);
final generator = ProjectApiGenerator();
try {
await generator.generateProjectApi();
print('\n🎉 API 代码生成完成!');
} catch (e, stackTrace) {
print('❌ 生成失败: $e');
print('堆栈跟踪: $stackTrace');
exit(1);
}
}
/// API
class ProjectApiGenerator {
late PerformanceParser parser;
late OptimizedRetrofitGenerator generator;
late EnhancedValidator validator;
ProjectApiGenerator() {
_initializeComponents();
}
///
void _initializeComponents() {
//
parser = PerformanceParser(
config: ParseConfig(
enablePerformanceStats: true,
enableParallelParsing: false, //
enableCaching: ProjectConfig.enableCaching,
maxConcurrency: 4,
enableMemoryOptimization: true,
),
);
//
generator = OptimizedRetrofitGenerator(
className: ProjectConfig.apiServiceName,
generateModularApis: ProjectConfig.enableModularApis,
generateBaseResult: ProjectConfig.enableBaseResult,
generatePagination: ProjectConfig.enablePagination,
generateFileUpload: ProjectConfig.enableFileUpload,
baseResultType: 'ApiResult',
pageResultType: 'PagedApiResult',
);
//
validator = EnhancedValidator(
includeWarnings: true,
);
}
/// API
Future<void> generateProjectApi() async {
// 1.
await _checkEnvironment();
// 2.
final document = await _parseSwaggerDocument();
// 3.
await _validateDocument(document);
// 4. API
await _generateApiCode(document);
// 5.
await _generateConfigFiles();
// 6. 使
await _generateUsageExamples();
// 7.
await _generateDocumentation(document);
// 8.
_showSummary();
}
///
Future<void> _checkEnvironment() async {
print('🔍 检查环境...');
// swagger.json
final swaggerFile = File(ProjectConfig.swaggerFile);
if (!swaggerFile.existsSync()) {
throw Exception('找不到 ${ProjectConfig.swaggerFile} 文件');
}
//
final outputDir = Directory(ProjectConfig.outputDir);
if (!outputDir.existsSync()) {
print('📁 创建输出目录: ${ProjectConfig.outputDir}');
outputDir.createSync(recursive: true);
}
//
final pubspecFile = File('pubspec.yaml');
if (pubspecFile.existsSync()) {
final content = await pubspecFile.readAsString();
final requiredDeps = ['dio', 'retrofit', 'json_annotation'];
final missingDeps = <String>[];
for (final dep in requiredDeps) {
if (!content.contains(dep)) {
missingDeps.add(dep);
}
}
if (missingDeps.isNotEmpty) {
print('⚠️ 缺少依赖: ${missingDeps.join(', ')}');
print(' 请在 pubspec.yaml 中添加这些依赖');
}
}
print('✅ 环境检查完成');
}
/// Swagger
Future<SwaggerDocument> _parseSwaggerDocument() async {
print('\n📖 解析 Swagger 文档...');
final jsonString = await File(ProjectConfig.swaggerFile).readAsString();
print(' - 文档大小: ${(jsonString.length / 1024).toStringAsFixed(2)}KB');
final stopwatch = Stopwatch()..start();
final document = await parser.parseDocument(jsonString);
stopwatch.stop();
print('✅ 解析完成');
print(' - 解析时间: ${stopwatch.elapsedMilliseconds}ms');
print(' - API 标题: ${document.title}');
print(' - API 版本: ${document.version}');
print(' - 路径数量: ${document.paths.length}');
print(' - 模型数量: ${document.models.length}');
print(' - 服务器数量: ${document.servers.length}');
//
final stats = parser.lastStats;
if (stats != null) {
print(' - 处理速度: ${stats.pathsPerSecond.toStringAsFixed(1)} paths/s');
print(
' - 吞吐量: ${(stats.bytesPerSecond / 1024).toStringAsFixed(2)} KB/s');
}
return document;
}
///
Future<void> _validateDocument(SwaggerDocument document) async {
if (!ProjectConfig.enableValidation) {
print('\n⏭️ 跳过文档验证');
return;
}
print('\n✅ 验证文档...');
final isValid = validator.validateDocument(document);
final errors =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.error);
final warnings =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.warning);
final criticalErrors =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.critical);
print(' - 验证结果: ${isValid ? "✅ 通过" : "❌ 失败"}');
print(' - 严重错误: ${criticalErrors.length}');
print(' - 错误: ${errors.length}');
print(' - 警告: ${warnings.length}');
if (criticalErrors.isNotEmpty) {
print('\n🚨 严重错误:');
for (final error in criticalErrors.take(3)) {
print(' - ${error.title}: ${error.description}');
}
throw Exception('文档包含严重错误,无法继续生成');
}
if (errors.isNotEmpty) {
print('\n❌ 错误:');
for (final error in errors.take(3)) {
print(' - ${error.title}: ${error.description}');
}
}
if (warnings.isNotEmpty) {
print('\n⚠️ 警告:');
for (final warning in warnings.take(3)) {
print(' - ${warning.title}: ${warning.description}');
}
}
//
final report = validator.errorReporter.generateReport();
final reportFile = File('${ProjectConfig.outputDir}/validation_report.txt');
await reportFile.writeAsString(report);
print(' - 验证报告: ${reportFile.path}');
}
/// API
Future<void> _generateApiCode(SwaggerDocument document) async {
print('\n🔧 生成 API 代码...');
final stopwatch = Stopwatch()..start();
final generatedCode = generator.generateFromDocument(document);
stopwatch.stop();
print('✅ 代码生成完成');
print(' - 生成时间: ${stopwatch.elapsedMilliseconds}ms');
print(' - 代码大小: ${(generatedCode.length / 1024).toStringAsFixed(2)}KB');
print(' - 代码行数: ${generatedCode.split('\n').length}');
// API
final apiFile = File(
'${ProjectConfig.outputDir}/${ProjectConfig.apiServiceName.toLowerCase()}.dart');
await apiFile.writeAsString(generatedCode);
print(' - API 文件: ${apiFile.path}');
//
final features = <String>[];
if (generatedCode.contains('class ApiResult')) features.add('基础响应类型');
if (generatedCode.contains('class PagedApiResult')) features.add('分页支持');
if (generatedCode.contains('MultipartFile')) features.add('文件上传');
if (generatedCode.contains('class ApiUtils')) features.add('工具类');
if (features.isNotEmpty) {
print(' - 生成特性: ${features.join(', ')}');
}
}
///
Future<void> _generateConfigFiles() async {
print('\n⚙️ 生成配置文件...');
// API
final apiConfig = '''
/// API
///
class ApiConfig {
static const String baseUrl = '${ProjectConfig.baseUrl}';
static const String apiVersion = '${ProjectConfig.apiVersion}';
static const int connectTimeout = 30000;
static const int receiveTimeout = 30000;
static const int sendTimeout = 30000;
//
static const Map<String, String> defaultHeaders = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
//
static const bool debugMode = true;
}
''';
final configFile = File('${ProjectConfig.outputDir}/api_config.dart');
await configFile.writeAsString(apiConfig);
print(' - API 配置: ${configFile.path}');
// Dio
final dioConfig = '''
/// Dio
import 'package:dio/dio.dart';
import 'api_config.dart';
class DioConfig {
static Dio createDio() {
final dio = Dio(BaseOptions(
baseUrl: ApiConfig.baseUrl,
connectTimeout: Duration(milliseconds: ApiConfig.connectTimeout),
receiveTimeout: Duration(milliseconds: ApiConfig.receiveTimeout),
sendTimeout: Duration(milliseconds: ApiConfig.sendTimeout),
headers: ApiConfig.defaultHeaders,
));
if (ApiConfig.debugMode) {
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
requestHeader: true,
responseHeader: false,
));
}
return dio;
}
}
''';
final dioConfigFile = File('${ProjectConfig.outputDir}/dio_config.dart');
await dioConfigFile.writeAsString(dioConfig);
print(' - Dio 配置: ${dioConfigFile.path}');
}
/// 使
Future<void> _generateUsageExamples() async {
print('\n📝 生成使用示例...');
final example = '''
/// API 使
/// Flutter 使 API
import 'package:dio/dio.dart';
import 'generated/${ProjectConfig.apiServiceName.toLowerCase()}.dart';
import 'dio_config.dart';
class ApiExample {
late final ${ProjectConfig.apiServiceName} apiService;
ApiExample() {
final dio = DioConfig.createDio();
apiService = ${ProjectConfig.apiServiceName}(dio);
}
///
Future<void> getUsersExample() async {
try {
final response = await apiService.getUsers();
print('用户列表: \$response');
} catch (e) {
print('获取用户列表失败: \$e');
}
}
///
Future<void> createUserExample() async {
try {
final userData = {
'name': 'John Doe',
'email': 'john@example.com',
};
final response = await apiService.createUser(userData);
print('用户创建成功: \$response');
} catch (e) {
print('创建用户失败: \$e');
}
}
}
''';
final exampleFile = File('${ProjectConfig.outputDir}/api_example.dart');
await exampleFile.writeAsString(example);
print(' - 使用示例: ${exampleFile.path}');
}
///
Future<void> _generateDocumentation(SwaggerDocument document) async {
print('\n📚 生成文档...');
final docs = StringBuffer();
docs.writeln('# ${document.title} API 文档');
docs.writeln();
docs.writeln('版本: ${document.version}');
docs.writeln('描述: ${document.description}');
docs.writeln();
docs.writeln('## 服务器');
for (final server in document.servers) {
docs.writeln('- ${server.url}: ${server.description}');
}
docs.writeln();
docs.writeln('## API 端点');
docs.writeln('总计: ${document.paths.length} 个端点');
docs.writeln();
docs.writeln('## 数据模型');
docs.writeln('总计: ${document.models.length} 个模型');
docs.writeln();
docs.writeln('## 使用方法');
docs.writeln('请参考 `api_example.dart` 文件中的示例代码。');
final docsFile = File('${ProjectConfig.outputDir}/README.md');
await docsFile.writeAsString(docs.toString());
print(' - API 文档: ${docsFile.path}');
}
///
void _showSummary() {
print('\n📊 生成总结');
print('-' * 40);
print('✅ 项目: ${ProjectConfig.projectName}');
print('✅ API 服务: ${ProjectConfig.apiServiceName}');
print('✅ 输出目录: ${ProjectConfig.outputDir}');
print('✅ 模块化 API: ${ProjectConfig.enableModularApis ? "启用" : "禁用"}');
print('✅ 基础响应类型: ${ProjectConfig.enableBaseResult ? "启用" : "禁用"}');
print('✅ 分页支持: ${ProjectConfig.enablePagination ? "启用" : "禁用"}');
print('✅ 文件上传: ${ProjectConfig.enableFileUpload ? "启用" : "禁用"}');
print('\n🎯 下一步:');
print('1. 运行 `flutter packages pub run build_runner build` 生成序列化代码');
print('2. 在项目中导入生成的 API 文件');
print('3. 参考 `api_example.dart` 中的使用示例');
print('4. 查看 `README.md` 了解 API 详情');
}
}

View File

@ -0,0 +1,311 @@
/// Dio + Retrofit 使
/// 使 API
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
//
part 'dio_retrofit_usage.g.dart';
/// API
@RestApi(baseUrl: 'https://api.example.com/v1')
abstract class ApiService {
factory ApiService(Dio dio, {String? baseUrl}) = _ApiService;
///
@GET('/users/{id}')
Future<UserResponse> getUser(@Path('id') int id);
///
@POST('/users')
Future<UserResponse> createUser(@Body() CreateUserRequest request);
///
@POST('/users/{id}/avatar')
@MultiPart()
Future<UploadResponse> uploadAvatar(
@Path('id') int id,
@Part() MultipartFile avatar,
);
///
@GET('/users')
Future<UserListResponse> getUsers(
@Query('page') int page,
@Query('size') int size,
@Query('search') String? search,
);
///
@GET('/files/{id}')
@DioResponseType(ResponseType.bytes)
Future<List<int>> downloadFile(@Path('id') String id);
}
///
class UserResponse {
final int id;
final String name;
final String email;
final String? avatar;
UserResponse({
required this.id,
required this.name,
required this.email,
this.avatar,
});
factory UserResponse.fromJson(Map<String, dynamic> json) => UserResponse(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
avatar: json['avatar'] as String?,
);
}
///
class CreateUserRequest {
final String name;
final String email;
final String password;
CreateUserRequest({
required this.name,
required this.email,
required this.password,
});
Map<String, dynamic> toJson() => {
'name': name,
'email': email,
'password': password,
};
}
///
class UserListResponse {
final List<UserResponse> users;
final int total;
final int page;
final int size;
UserListResponse({
required this.users,
required this.total,
required this.page,
required this.size,
});
factory UserListResponse.fromJson(Map<String, dynamic> json) =>
UserListResponse(
users: (json['users'] as List)
.map((e) => UserResponse.fromJson(e as Map<String, dynamic>))
.toList(),
total: json['total'] as int,
page: json['page'] as int,
size: json['size'] as int,
);
}
///
class UploadResponse {
final String url;
final String filename;
final int size;
UploadResponse({
required this.url,
required this.filename,
required this.size,
});
factory UploadResponse.fromJson(Map<String, dynamic> json) => UploadResponse(
url: json['url'] as String,
filename: json['filename'] as String,
size: json['size'] as int,
);
}
/// API 使
class ApiClient {
late final Dio _dio;
late final ApiService _apiService;
ApiClient({String? baseUrl}) {
_dio = Dio();
_setupDio();
_apiService = ApiService(_dio, baseUrl: baseUrl);
}
/// Dio
void _setupDio() {
//
_dio.options.connectTimeout = const Duration(seconds: 30);
_dio.options.receiveTimeout = const Duration(seconds: 30);
_dio.options.sendTimeout = const Duration(seconds: 30);
//
_dio.interceptors.addAll([
//
LogInterceptor(
requestBody: true,
responseBody: true,
logPrint: (obj) => print(obj),
),
//
BearerTokenInterceptor(token: 'your-auth-token'),
// API Key
ApiKeyInterceptor(
apiKey: 'your-api-key',
location: ApiKeyLocation.header,
paramName: 'X-API-Key',
),
//
ErrorHandlerInterceptor(),
]);
}
///
Future<UserResponse> getUser(int id) async {
try {
return await _apiService.getUser(id);
} catch (e) {
throw _handleError(e);
}
}
///
Future<UserResponse> createUser({
required String name,
required String email,
required String password,
}) async {
try {
final request = CreateUserRequest(
name: name,
email: email,
password: password,
);
return await _apiService.createUser(request);
} catch (e) {
throw _handleError(e);
}
}
///
Future<UploadResponse> uploadAvatar(int userId, String filePath) async {
try {
final file = await FileUploadHandler.createImageFile(filePath: filePath);
return await _apiService.uploadAvatar(userId, file);
} catch (e) {
throw _handleError(e);
}
}
///
Future<UserListResponse> getUsers({
int page = 1,
int size = 20,
String? search,
}) async {
try {
return await _apiService.getUsers(page, size, search);
} catch (e) {
throw _handleError(e);
}
}
///
Future<void> downloadFile(String fileId, String savePath) async {
try {
final bytes = await _apiService.downloadFile(fileId);
final file = File(savePath);
await file.writeAsBytes(bytes);
} catch (e) {
throw _handleError(e);
}
}
///
Exception _handleError(dynamic error) {
if (error is DioException) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
return TimeoutException('请求超时,请检查网络连接');
case DioExceptionType.badResponse:
final statusCode = error.response?.statusCode;
final message = error.response?.data?['message'] ?? '服务器错误';
return HttpException('HTTP $statusCode: $message');
case DioExceptionType.cancel:
return Exception('请求已取消');
case DioExceptionType.connectionError:
return Exception('网络连接错误,请检查网络设置');
default:
return Exception('未知错误: ${error.message}');
}
}
return Exception('未知错误: $error');
}
///
void dispose() {
_dio.close();
}
}
/// 使
void main() async {
final apiClient = ApiClient(baseUrl: 'https://api.example.com/v1');
try {
//
final user = await apiClient.getUser(1);
print('用户信息: ${user.name} (${user.email})');
//
final newUser = await apiClient.createUser(
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
);
print('创建用户成功: ${newUser.id}');
//
final userList = await apiClient.getUsers(page: 1, size: 10);
print('用户总数: ${userList.total}');
//
// final uploadResult = await apiClient.uploadAvatar(1, '/path/to/avatar.jpg');
// print('头像上传成功: ${uploadResult.url}');
//
// await apiClient.downloadFile('file-id', '/path/to/save/file.pdf');
// print('文件下载完成');
} catch (e) {
print('API 调用失败: $e');
} finally {
apiClient.dispose();
}
}
///
class TimeoutException implements Exception {
final String message;
TimeoutException(this.message);
@override
String toString() => 'TimeoutException: $message';
}
class HttpException implements Exception {
final String message;
HttpException(this.message);
@override
String toString() => 'HttpException: $message';
}

View File

@ -0,0 +1,178 @@
//
import 'package:dio/dio.dart';
import 'package:json_annotation/json_annotation.dart';
//
import 'package:path/path.dart' as path;
//
part 'advanced_api_service_api.g.dart';
///
@JsonSerializable(genericArgumentFactories: true)
class ApiResult<T> {
///
final int code;
///
final String message;
///
final T? data;
///
bool get isSuccess => code == 200;
const ApiResult({
required this.code,
required this.message,
this.data,
});
factory ApiResult.fromJson(
Map<String, dynamic> json,
T Function(Object? json) fromJsonT,
) =>
_$ApiResultFromJson(json, fromJsonT);
Map<String, dynamic> toJson(Object Function(T value) toJsonT) =>
_$ApiResultToJson(this, toJsonT);
}
///
@JsonSerializable()
class BasePageParameter {
/// 1
final int page;
///
final int size;
const BasePageParameter({
this.page = 1,
this.size = 20,
});
factory BasePageParameter.fromJson(Map<String, dynamic> json) =>
_$BasePageParameterFromJson(json);
Map<String, dynamic> toJson() => _$BasePageParameterToJson(this);
}
///
@JsonSerializable(genericArgumentFactories: true)
class PagedResult<T> {
///
final List<T> list;
///
final int total;
///
final int page;
///
final int size;
///
int get totalPages => (total / size).ceil();
///
bool get hasNext => page < totalPages;
///
bool get hasPrevious => page > 1;
const PagedResult({
required this.list,
required this.total,
required this.page,
required this.size,
});
factory PagedResult.fromJson(
Map<String, dynamic> json,
T Function(Object? json) fromJsonT,
) =>
_$PagedResultFromJson(json, fromJsonT);
Map<String, dynamic> toJson(Object Function(T value) toJsonT) =>
_$PagedResultToJson(this, toJsonT);
}
///
@JsonSerializable()
class FileUploadRequest {
///
@JsonKey(includeFromJson: false, includeToJson: false)
final MultipartFile file;
///
final String? filename;
///
final String? contentType;
const FileUploadRequest({
required this.file,
this.filename,
this.contentType,
});
factory FileUploadRequest.fromJson(Map<String, dynamic> json) =>
_$FileUploadRequestFromJson(json);
Map<String, dynamic> toJson() => _$FileUploadRequestToJson(this);
}
///
@JsonSerializable()
class FileUploadResult {
/// URL
final String url;
///
final String filename;
///
final int size;
///
final String? contentType;
const FileUploadResult({
required this.url,
required this.filename,
required this.size,
this.contentType,
});
factory FileUploadResult.fromJson(Map<String, dynamic> json) =>
_$FileUploadResultFromJson(json);
Map<String, dynamic> toJson() => _$FileUploadResultToJson(this);
}
/// API
/// API
class AdvancedApiService {
final Dio _dio;
AdvancedApiService(this._dio, {String? baseUrl});
}
/// API
class ApiUtils {
///
static Future<MultipartFile> createFileUpload(String filePath) async {
return MultipartFile.fromFile(
filePath,
filename: path.basename(filePath),
);
}
///
static BasePageParameter createPageParam({int page = 1, int size = 20}) {
return BasePageParameter(page: page, size: size);
}
}

View File

@ -0,0 +1,70 @@
// API - Tag API
// Swagger API :
// xy_swagger_generator by max
// Copyright (C) 2025 YuanXuan. All rights reserved.
import 'package:dio/dio.dart';
import 'package:learning_officer_oa/common/models/common/base_page_result.dart';
import 'package:learning_officer_oa/common/models/common/base_result.dart';
import 'api_error.dart';
import 'api_error_handler.dart';
/// API客户端类
/// API接口访
class BasicApiService {
final Dio _dio;
BasicApiService(this._dio, {String? baseUrl});
/// Dio实例
Dio get dio => _dio;
/// token
void setAuthToken(String token) {
_dio.options.headers['Authorization'] = 'Bearer $token';
}
/// token
void clearAuthToken() {
_dio.options.headers.remove('Authorization');
}
/// URL
void setBaseUrl(String baseUrl) {
_dio.options.baseUrl = baseUrl;
}
///
void addRequestInterceptor(Interceptor interceptor) {
_dio.interceptors.add(interceptor);
}
///
void addResponseInterceptor(Interceptor interceptor) {
_dio.interceptors.add(interceptor);
}
///
void addErrorInterceptor(Interceptor interceptor) {
_dio.interceptors.add(interceptor);
}
/// API调用
Future<T> callWithErrorHandling<T>(Future<T> Function() apiCall) async {
try {
return await apiCall();
} on DioException catch (e) {
final error = ApiErrorHandler.handleDioError(e);
throw error;
} catch (e) {
final error = ApiError(
type: ApiErrorType.unknown,
message: '未知错误: $e',
code: -1,
originalError: e,
);
throw error;
}
}
}

View File

@ -0,0 +1,79 @@
{
"timestamp": "2025-07-24T07:16:17.845544",
"summary": {
"total": 3,
"by_severity": {
"warning": 1,
"error": 1,
"info": 1
}
},
"errors": [
{
"id": "MISSING_INFO_DESCRIPTION",
"title": "Missing API Description",
"description": "API description helps users understand the purpose of your API.",
"severity": "warning",
"category": "bestPractice",
"location": {
"json_path": "info.description",
"line": null,
"column": null,
"snippet": null
},
"suggestions": [
{
"description": "Add a description explaining what your API does",
"code_example": "\"description\": \"This API provides user management functionality\"",
"documentation_url": null
}
],
"related_errors": [],
"timestamp": "2025-07-24T07:16:17.842816"
},
{
"id": "EMPTY_PATHS",
"title": "Empty Paths Object",
"description": "OpenAPI document must contain at least one path.",
"severity": "error",
"category": "validation",
"location": {
"json_path": "paths",
"line": null,
"column": null,
"snippet": null
},
"suggestions": [
{
"description": "Add at least one API endpoint",
"code_example": "\"/users\": { \"get\": { \"responses\": { \"200\": { \"description\": \"Success\" } } } }",
"documentation_url": null
}
],
"related_errors": [],
"timestamp": "2025-07-24T07:16:17.842917"
},
{
"id": "NO_OPERATION_TAGS",
"title": "No Operation Tags",
"description": "Consider using tags to organize your API operations.",
"severity": "info",
"category": "bestPractice",
"location": {
"json_path": "paths",
"line": null,
"column": null,
"snippet": null
},
"suggestions": [
{
"description": "Add tags to operations",
"code_example": "\"tags\": [\"users\"]",
"documentation_url": null
}
],
"related_errors": [],
"timestamp": "2025-07-24T07:16:17.843223"
}
]
}

View File

@ -0,0 +1,52 @@
📊 Error Summary
==================================================
⚠️ WARNING: 1
❌ ERROR: 1
INFO: 1
📂 Best Practice
------------------------------
⚠️ WARNING: Missing API Description
Category: Best Practice
Location: info.description
Description:
API description helps users understand the purpose of your API.
Suggestions:
1. Add a description explaining what your API does
Example:
"description": "This API provides user management functionality"
INFO: No Operation Tags
Category: Best Practice
Location: paths
Description:
Consider using tags to organize your API operations.
Suggestions:
1. Add tags to operations
Example:
"tags": ["users"]
📂 Validation Error
------------------------------
❌ ERROR: Empty Paths Object
Category: Validation Error
Location: paths
Description:
OpenAPI document must contain at least one path.
Suggestions:
1. Add at least one API endpoint
Example:
"/users": { "get": { "responses": { "200": { "description": "Success" } } } }

View File

@ -1,286 +0,0 @@
# Swagger 代码生成器配置文件模板
# 复制此文件到您的项目根目录,重命名为 generator_config.yaml并根据需要修改
# 基本配置
generator:
name: "my_project_generator" # 修改为您的项目生成器名称
version: "1.0"
author: "Your Name" # 修改为您的名字
copyright: "Copyright (C) 2025 Your Company. All rights reserved." # 修改为您的版权信息
# 输入配置
input:
# Swagger 文档源配置(支持多版本)
swagger_urls:
# 简写形式:直接列出 URL
- "https://your-api.com/swagger/v1/swagger.json"
- "https://your-api.com/swagger/v2/swagger.json"
# 完整形式:可以控制每个版本的启用状态
# - url: "https://your-api.com/swagger/v1/swagger.json"
# enabled: true # 可选,是否启用此版本(默认: true
# 验证配置
validate_schema: true
strict_mode: true
# 输出配置
output:
# 输出目录(根据您的项目结构修改)
base_dir: "./lib/generated"
api_dir: "./lib/generated/api"
models_dir: "./lib/generated/api_models"
# 文件命名
api_file_suffix: "_api.dart"
model_file_suffix: ".dart"
# 是否按 tag 分组
split_by_tags: true
# 跳过的目录列表(这些目录下的文件将不会被生成)
# 支持相对路径和绝对路径,支持目录名或完整路径
ignored_directories:
# - "api/v1" # 跳过 v1 版本的 API
# - "api_models/request" # 跳过请求模型目录
# - "./lib/generated/api/v2" # 跳过特定路径
# 跳过的文件名列表(这些文件将不会被生成)
# 支持精确匹配、通配符匹配和模式匹配
ignored_files:
# 精确匹配文件名
# - "user_api.dart" # 跳过名为 user_api.dart 的文件
# - "mobile_manager_api.dart" # 跳过指定文件
# 通配符匹配(支持前缀和后缀)
# - "*_api.dart" # 跳过所有以 _api.dart 结尾的文件
# - "user*.dart" # 跳过所有以 user 开头的 .dart 文件
# - "*manager*" # 跳过所有包含 manager 的文件名
# 示例:跳过所有 v1 版本的 API 文件
# - "*_api_v1.dart"
# - "*V1*.dart"
# 代码生成配置
generation:
# API 接口配置
api:
enabled: true
use_retrofit: true
use_dio: true
parser: "JsonSerializable"
# 版本提取配置(多版本支持)
version_extraction:
# 版本提取正则表达式模式
# 默认: "/api/v(\\d+)/" 匹配 /api/v1/, /api/v2/ 等
pattern: "/api/v(\\d+)/"
# 默认版本(当无法从路径提取版本时使用)
default_version: "v1"
# 基础类型配置(根据您的项目调整)
base_result_type: "BaseResult"
base_page_result_type: "BasePageResult"
base_result_import: "package:your_project/common/models/base_result.dart"
base_page_result_import: "package:your_project/common/models/base_page_result.dart"
# 方法命名
method_naming: "camelCase" # camelCase, snake_case
# 数据模型配置
models:
enabled: true
use_json_serializable: true
# JsonSerializable 配置
json_serializable:
checked: true
include_if_null: false
explicit_to_json: true
# 类命名
class_naming: "PascalCase" # PascalCase, snake_case
field_naming: "camelCase" # camelCase, snake_case
# 构造函数配置
use_const_constructor: true
required_for_non_nullable: true
# 类型映射配置
type_mapping:
# OpenAPI -> Dart 类型映射
string: "String"
integer: "int"
number: "double"
boolean: "bool"
array: "List"
object: "Map<String, dynamic>"
# 特殊类型处理
date: "DateTime"
date-time: "DateTime"
binary: "Uint8List"
# 导入管理配置
imports:
# 按需导入
on_demand: true
# 自动排序
auto_sort: true
# 分组导入
group_imports: true
# 标准库导入
dart_imports:
- "dart:convert"
- "dart:typed_data"
# 第三方库导入
package_imports:
- "package:dio/dio.dart"
- "package:retrofit/retrofit.dart"
- "package:json_annotation/json_annotation.dart"
# 验证配置
validation:
# 严格模式
strict_mode: true
# 检查项
checks:
- "schema_exists"
- "ref_resolution"
- "type_consistency"
- "nullable_correctness"
- "required_fields"
# 错误处理
on_error: "warn" # fail, warn, ignore
# 警告处理
on_warning: "log" # fail, log, ignore
# 优化配置
optimization:
# 代码优化
remove_unused_imports: true
optimize_imports: true
# 性能优化
cache_schemas: true
parallel_generation: false
# 内存优化
lazy_loading: true
# 调试配置
debug:
# 详细日志
verbose: false
# 调试输出
debug_output: false
# 性能监控
performance_monitoring: false
# 生成统计
generation_stats: true
# 兼容性配置
compatibility:
# Dart 版本
dart_version: ">=3.0.0"
# Flutter 版本
flutter_version: ">=3.10.0"
# 依赖版本
dependencies:
dio: "^5.0.0"
retrofit: "^4.0.0"
json_annotation: "^4.8.0"
dev_dependencies:
build_runner: "^2.3.0"
retrofit_generator: "^8.0.0"
json_serializable: "^6.6.0"
# 自定义配置
custom:
# 项目特定配置
project_name: "your_project" # 修改为您的项目名称
# 特殊处理规则
special_handling:
# 健康检查接口返回 void
health_check_paths:
- "/health"
- "/healthcheck"
- "/api/health"
# 分页接口参数名
pagination_params:
- "page"
- "size"
- "limit"
- "offset"
- "pageIndex"
- "pageSize"
# 忽略的路径
ignored_paths:
- "/swagger"
- "/docs"
# 忽略的 tags
ignored_tags:
- "Internal"
- "Debug"
# 模板配置
templates:
# 文件头模板
# 支持模板变量:
# {fileName} - 文件名(如 "user_api.dart"
# {fileType} - 文件类型描述(如 "API 接口定义"、"模型定义"
# {swaggerUrl} - Swagger 文档 URL
# {generatorName} - 生成器名称(从 generator.name 读取)
# {author} - 作者(从 generator.author 读取)
# {copyright} - 版权信息(从 generator.copyright 读取)
file_header: |
// {fileType}
// 基于 Swagger API 文档: {swaggerUrl}
// 由 {generatorName} by {author} 生成
// {copyright}
# API 类模板
# 支持模板变量:
# {tagName} - Tag 名称(如 "User"、"Task"
# {className} - 类名(如 "UserApi"、"TaskApi"
api_class: |
/// {tagName} API 接口
/// 负责处理 {tagName} 相关的接口
@RestApi(parser: Parser.JsonSerializable)
abstract class {className} {
factory {className}(Dio dio, {String? baseUrl}) = _{className};
}
# 模型类模板
# 支持模板变量:
# {className} - 类名(如 "User"、"Task"
# {constructorParams} - 构造函数参数列表
model_class: |
@JsonSerializable(checked: true, includeIfNull: false)
class {className} {
const {className}({constructorParams});
factory {className}.fromJson(Map<String, dynamic> json) =>
_${className}FromJson(json);
Map<String, dynamic> toJson() => _${className}ToJson(this);
}

View File

@ -212,15 +212,8 @@ custom:
# 模板配置 # 模板配置
templates: templates:
# 文件头模板 # 文件头模板
# 支持模板变量:
# {fileName} - 文件名(如 "user_api.dart"
# {fileType} - 文件类型描述(如 "API 接口定义"、"模型定义"
# {swaggerUrl} - Swagger 文档 URL
# {generatorName} - 生成器名称(从 generator.name 读取)
# {author} - 作者(从 generator.author 读取)
# {copyright} - 版权信息(从 generator.copyright 读取)
file_header: | file_header: |
// {fileType} // {fileName} {fileType}
// 基于 Swagger API 文档: {swaggerUrl} // 基于 Swagger API 文档: {swaggerUrl}
// 由 {generatorName} by {author} 生成 // 由 {generatorName} by {author} 生成
// {copyright} // {copyright}

View File

@ -2,10 +2,9 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../core/config.dart';
import '../core/config_loader.dart';
import '../core/models.dart'; import '../core/models.dart';
import '../generators/documentation_generator.dart'; import '../generators/documentation_generator.dart';
import '../generators/endpoint_code_generator.dart';
import '../generators/model_code_generator.dart'; import '../generators/model_code_generator.dart';
import '../generators/retrofit_api_generator.dart'; import '../generators/retrofit_api_generator.dart';
import '../parsers/swagger_data_parser.dart'; import '../parsers/swagger_data_parser.dart';
@ -26,6 +25,12 @@ class GenerateCommand extends BaseCommand {
@override @override
List<CommandOption> get options => [ List<CommandOption> get options => [
const CommandOption(
name: 'endpoints',
shortName: 'e',
description: '生成API端点常量',
type: OptionType.flag,
),
const CommandOption( const CommandOption(
name: 'models', name: 'models',
shortName: 'm', shortName: 'm',
@ -79,42 +84,10 @@ class GenerateCommand extends BaseCommand {
await prepare(parsedArgs); await prepare(parsedArgs);
// Swagger // Swagger文档
progress('正在解析 ${SwaggerConfig.swaggerJsonUrls.length} 个 Swagger 文档...'); progress('正在解析Swagger文档...');
final parser = SwaggerDataParser(); final parser = SwaggerDataParser();
final document = await parser.fetchAndParseSwaggerDocument();
// paths models
SwaggerDocument? mergedDocument;
for (int i = 0; i < SwaggerConfig.swaggerJsonUrls.length; i++) {
final url = SwaggerConfig.swaggerJsonUrls[i];
progress(
' [${i + 1}/${SwaggerConfig.swaggerJsonUrls.length}] 正在解析: $url');
final doc = await parser.fetchAndParseSwaggerDocument(url);
if (mergedDocument == null) {
mergedDocument = doc;
} else {
// paths models
mergedDocument = SwaggerDocument(
title: mergedDocument.title,
description: mergedDocument.description,
version: '${mergedDocument.version} + ${doc.version}',
paths: {...mergedDocument.paths, ...doc.paths},
models: {...mergedDocument.models, ...doc.models},
controllers: {...mergedDocument.controllers, ...doc.controllers},
);
}
}
if (mergedDocument == null) {
print('❌ 没有成功解析任何 Swagger 文档');
return 1;
}
final document = mergedDocument;
success('成功合并 ${SwaggerConfig.swaggerJsonUrls.length} 个 Swagger 文档');
// //
final options = _parseGenerateOptions(parsedArgs); final options = _parseGenerateOptions(parsedArgs);
@ -127,6 +100,18 @@ class GenerateCommand extends BaseCommand {
int generatedFiles = 0; int generatedFiles = 0;
//
if (options.generateEndpoints) {
progress('正在生成API端点常量...');
final generator = EndpointCodeGenerator(document);
final code = generator.generate();
final filePath = '$fullOutputDir/api_paths.dart';
await FileUtils.writeFile(filePath, code);
success('API端点常量已保存到: $filePath');
generatedFiles++;
}
// //
if (options.generateModels) { if (options.generateModels) {
progress('正在生成数据模型...'); progress('正在生成数据模型...');
@ -142,13 +127,6 @@ class GenerateCommand extends BaseCommand {
for (final entry in modelFiles.entries) { for (final entry in modelFiles.entries) {
final filePath = '$modelsDir/${entry.key}'; final filePath = '$modelsDir/${entry.key}';
//
if (ConfigLoader.shouldSkipFile(filePath)) {
progress('跳过文件: $filePath');
continue;
}
await FileUtils.writeFile(filePath, entry.value); await FileUtils.writeFile(filePath, entry.value);
success('模型文件已保存到: $filePath'); success('模型文件已保存到: $filePath');
generatedFiles++; generatedFiles++;
@ -157,150 +135,54 @@ class GenerateCommand extends BaseCommand {
// Retrofit API 使 // Retrofit API 使
if (options.generateApi) { if (options.generateApi) {
progress('正在按版本和tags分组生成Retrofit风格API接口...'); progress('正在按tags分组生成Retrofit风格API接口...');
final apiDir = '$fullOutputDir/api';
await FileUtils.ensureDirectoryExists(apiDir);
// 🎯 paths
final pathsByVersion = <String, List<ApiPath>>{};
for (final path in document.paths.values) {
final version = _extractVersionFromPath(path.path);
pathsByVersion.putIfAbsent(version, () => []).add(path);
}
progress(
'检测到 ${pathsByVersion.keys.length} 个版本: ${pathsByVersion.keys.join(", ")}');
// API
final versionedFiles = <String, Map<String, String>>{};
for (final versionEntry in pathsByVersion.entries) {
final version = versionEntry.key;
final versionPaths = versionEntry.value;
progress(' 正在生成 $version 版本 API${versionPaths.length} 个接口)...');
//
final versionDocument = SwaggerDocument(
title: document.title,
description: document.description,
version: document.version,
paths: {for (var p in versionPaths) p.path: p},
models: document.models,
controllers: document.controllers,
);
//
final generator = RetrofitApiGenerator( final generator = RetrofitApiGenerator(
className: 'ApiClient', className: 'ApiClient',
useRetrofit: true, useRetrofit: true,
useDio: true, useDio: true,
splitByTags: true, splitByTags: true, // 使
versionedApi: true,
); );
generator.document = versionDocument; //
generator.document = document;
//
generator.ensureParameterEntitiesGenerated(); generator.ensureParameterEntitiesGenerated();
// API // tags API
final tagApiFiles = generator.generateApiFilesByTags(); final tagApiFiles = generator.generateApiFilesByTags();
versionedFiles[version] = {}; final apiDir = '$fullOutputDir/api';
await FileUtils.ensureDirectoryExists(apiDir);
for (final entry in tagApiFiles.entries) { for (final entry in tagApiFiles.entries) {
final fileName = entry.key; final fileName = entry.key;
var code = entry.value; final code = entry.value;
final filePath = '$apiDir/$fileName';
//
code = _addVersionSuffixToCode(code, version);
versionedFiles[version]![fileName] = code;
}
}
//
for (final versionEntry in versionedFiles.entries) {
final version = versionEntry.key;
final files = versionEntry.value;
final versionDir = '$apiDir/$version';
//
if (ConfigLoader.shouldSkipFile(versionDir)) {
progress('跳过版本目录: $versionDir');
continue;
}
await FileUtils.ensureDirectoryExists(versionDir);
for (final fileEntry in files.entries) {
final fileName = fileEntry.key;
final code = fileEntry.value;
final filePath = '$versionDir/$fileName';
//
if (ConfigLoader.shouldSkipFile(filePath)) {
progress('跳过文件: $filePath');
continue;
}
await FileUtils.writeFile(filePath, code); await FileUtils.writeFile(filePath, code);
success('API接口文件已保存到: $filePath'); success('API接口文件已保存到: $filePath');
generatedFiles++; generatedFiles++;
} }
// index.dart // API
if (!ConfigLoader.shouldSkipFile(versionDir)) { final mainCode = generator.generateMainApiFile();
await _generateVersionIndexFile(versionDir, files.keys.toList());
success('$version/index.dart 已生成');
}
}
// API ApiClient
final mainCode = _generateVersionedApiClient(versionedFiles);
final mainFilePath = '$apiDir/api_client.dart'; final mainFilePath = '$apiDir/api_client.dart';
// API
if (!ConfigLoader.shouldSkipFile(mainFilePath)) {
await FileUtils.writeFile(mainFilePath, mainCode); await FileUtils.writeFile(mainFilePath, mainCode);
success('主API接口文件已保存到: $mainFilePath'); success('主API接口文件已保存到: $mainFilePath');
generatedFiles++; generatedFiles++;
} else {
progress('跳过文件: $mainFilePath');
}
// 使 //
final lastGenerator = RetrofitApiGenerator( final parameterEntityFiles = generator.generateParameterEntityFiles();
className: 'ApiClient',
useRetrofit: true,
useDio: true,
);
lastGenerator.document = document;
lastGenerator.ensureParameterEntitiesGenerated();
final parameterEntityFiles =
lastGenerator.generateParameterEntityFiles();
if (parameterEntityFiles.isNotEmpty) { if (parameterEntityFiles.isNotEmpty) {
final modelsDir = '$fullOutputDir/api_models'; final modelsDir = '$fullOutputDir/api_models';
// Parameters parameters await FileUtils.ensureDirectoryExists(modelsDir);
final parametersDir = '$modelsDir/parameters';
await FileUtils.ensureDirectoryExists(parametersDir);
for (final entry in parameterEntityFiles.entries) { for (final entry in parameterEntityFiles.entries) {
final filePath = '$parametersDir/${entry.key}'; final filePath = '$modelsDir/${entry.key}';
//
if (ConfigLoader.shouldSkipFile(filePath)) {
progress('跳过文件: $filePath');
continue;
}
await FileUtils.writeFile(filePath, entry.value); await FileUtils.writeFile(filePath, entry.value);
success('参数实体类文件已保存到: $filePath'); success('参数实体类文件已保存到: $filePath');
generatedFiles++; generatedFiles++;
} }
// parameters/index.dart
await _generateSubDirectoryIndexFile(parametersDir);
} }
} }
@ -322,15 +204,9 @@ class GenerateCommand extends BaseCommand {
final code = generator.generate(); final code = generator.generate();
final filePath = '$fullOutputDir/api_documentation.md'; final filePath = '$fullOutputDir/api_documentation.md';
//
if (!ConfigLoader.shouldSkipFile(filePath)) {
await FileUtils.writeFile(filePath, code); await FileUtils.writeFile(filePath, code);
success('API文档已保存到: $filePath'); success('API文档已保存到: $filePath');
generatedFiles++; generatedFiles++;
} else {
progress('跳过文件: $filePath');
}
} }
// //
@ -346,11 +222,15 @@ class GenerateCommand extends BaseCommand {
/// ///
GenerateOptions _parseGenerateOptions(ParsedArguments args) { GenerateOptions _parseGenerateOptions(ParsedArguments args) {
final hasAnyFlag = args.hasOption('models') || final hasAnyFlag = args.hasOption('endpoints') ||
args.hasOption('models') ||
args.hasOption('docs') || args.hasOption('docs') ||
args.hasOption('api'); args.hasOption('api');
return GenerateOptions( return GenerateOptions(
generateEndpoints: hasAnyFlag
? (args.getOption<bool>('endpoints') ?? false)
: (args.getOption<bool>('all') ?? true),
generateModels: hasAnyFlag generateModels: hasAnyFlag
? (args.getOption<bool>('models') ?? false) ? (args.getOption<bool>('models') ?? false)
: (args.getOption<bool>('all') ?? true), : (args.getOption<bool>('all') ?? true),
@ -373,53 +253,6 @@ class GenerateCommand extends BaseCommand {
return []; return [];
} }
final files = await directory.list().toList();
final exportPaths = <String>[];
for (final entity in files) {
if (entity is Directory) {
// index.dart
final dirName = path.basename(entity.path);
// index.dart
final subIndexPath = path.join(entity.path, 'index.dart');
if (await File(subIndexPath).exists()) {
exportPaths.add('$dirName/index.dart');
}
} else if (entity is File && entity.path.endsWith('.dart')) {
final fileName = path.basename(entity.path);
// index.dart .g.dart
if (fileName != 'index.dart' && !fileName.endsWith('.g.dart')) {
// api_models Parameters
exportPaths.add(fileName);
}
}
}
//
exportPaths.sort((a, b) {
final aIsDir = a.contains('/');
final bIsDir = b.contains('/');
if (aIsDir && !bIsDir) return -1;
if (!aIsDir && bIsDir) return 1;
return a.compareTo(b);
});
return exportPaths;
} catch (e) {
print('获取模型文件列表失败: $e');
return [];
}
}
/// index.dart
Future<void> _generateSubDirectoryIndexFile(String subDir) async {
try {
final directory = Directory(subDir);
if (!await directory.exists()) return;
final dirName = path.basename(subDir);
// .dart
final files = await directory.list().toList(); final files = await directory.list().toList();
final dartFiles = <String>[]; final dartFiles = <String>[];
@ -435,28 +268,10 @@ class GenerateCommand extends BaseCommand {
// //
dartFiles.sort(); dartFiles.sort();
return dartFiles;
// index.dart
final buffer = StringBuffer();
buffer.writeln('// 模型导出文件');
buffer.writeln('// 基于 Swagger API 文档: ');
buffer.writeln('// 由 xy_swagger_generator by max 生成');
buffer.writeln('// Copyright (C) 2025 YuanXuan. All rights reserved.');
buffer.writeln('');
buffer.writeln('');
buffer.writeln('library;');
buffer.writeln('');
for (final fileName in dartFiles) {
buffer.writeln('export \'$fileName\';');
}
//
final indexPath = path.join(subDir, 'index.dart');
await FileUtils.writeFile(indexPath, buffer.toString());
success('$dirName/index.dart 已生成,包含 ${dartFiles.length} 个文件');
} catch (e) { } catch (e) {
print('生成 index.dart 失败: $e'); print('获取模型文件列表失败: $e');
return [];
} }
} }
@ -503,225 +318,11 @@ class GenerateCommand extends BaseCommand {
FileUtils.writeFile('$outputDir/SUMMARY.md', summary.toString()); FileUtils.writeFile('$outputDir/SUMMARY.md', summary.toString());
} }
/// API
/// : /api/v1/User/GetData v1
/// /api/v2/User/GetData v2
/// /api/User/GetData v1 ()
/// 使
String _extractVersionFromPath(String path) {
//
final pattern = ConfigLoader.getVersionExtractionPattern();
final defaultVersion = ConfigLoader.getDefaultVersion();
try {
final versionMatch = RegExp(pattern).firstMatch(path);
if (versionMatch != null && versionMatch.groupCount > 0) {
return 'v${versionMatch.group(1)}';
}
} catch (e) {
// 使
final defaultPattern = r'/api/v(\d+)/';
final versionMatch = RegExp(defaultPattern).firstMatch(path);
if (versionMatch != null) {
return 'v${versionMatch.group(1)}';
}
}
return defaultVersion;
}
///
/// V1: MobileManagerApi
/// V2: MobileManagerApiV2V2后缀
String _addVersionSuffixToCode(String code, String version) {
// V1
if (version == 'v1') {
return code;
}
final versionUpper = version.toUpperCase(); // v2 V2, v3 V3
// abstract class
code = code.replaceAllMapped(
RegExp(r'abstract class (\w+Api)\b'),
(match) => 'abstract class ${match.group(1)}$versionUpper',
);
// factory
code = code.replaceAllMapped(
RegExp(r'factory (\w+Api)\('),
(match) => 'factory ${match.group(1)}$versionUpper(',
);
// = _XXXApi
code = code.replaceAllMapped(
RegExp(r'= _(\w+Api);'),
(match) => '= _${match.group(1)}$versionUpper;',
);
// part
code = code.replaceAllMapped(
RegExp(r"part '(\w+)\.g\.dart';"),
(match) => "part '${match.group(1)}.g.dart';",
);
// import API
code = code.replaceAllMapped(
RegExp(r"import '../(\w+_api)\.dart';"),
(match) => "import '../$version/${match.group(1)}.dart';",
);
return code;
}
/// ApiClient
String _generateVersionedApiClient(
Map<String, Map<String, String>> versionedFiles) {
final buffer = StringBuffer();
//
buffer.writeln('// 统一 API 客户端');
buffer.writeln('// 支持多版本 API 管理');
buffer.writeln('// 由 xy_swagger_generator by max 生成');
buffer.writeln('// Copyright (C) 2025 YuanXuan. All rights reserved.');
buffer.writeln();
buffer.writeln('import \'package:dio/dio.dart\';');
buffer.writeln();
// API
final apiClasses = <String, Set<String>>{}; // version -> class names
for (final versionEntry in versionedFiles.entries) {
final version = versionEntry.key;
final files = versionEntry.value;
apiClasses[version] = {};
for (final fileName in files.keys) {
// : mobile_manager_api.dart MobileManagerApi
final className = fileName
.replaceAll('.dart', '')
.split('_')
.map((word) => word[0].toUpperCase() + word.substring(1))
.join('');
apiClasses[version]!.add(className);
}
}
// index.dart
final versions = apiClasses.keys.toList()..sort();
for (final version in versions) {
buffer.writeln('import \'$version/index.dart\';');
}
buffer.writeln();
// ApiClient
buffer.writeln('/// 统一 API 客户端');
buffer.writeln('/// 支持多版本 API 访问');
buffer.writeln('class ApiClient {');
buffer.writeln(' final Dio _dio;');
buffer.writeln();
// API
for (final versionEntry in apiClasses.entries) {
final version = versionEntry.key;
final versionUpper =
version == 'v1' ? '' : version.toUpperCase(); // v1不加后缀
for (final className in versionEntry.value) {
final suffix = version == 'v1' ? '' : versionUpper;
buffer.writeln(
' late final $className$suffix _${_toLowerCamelCase(className)}$suffix;');
}
}
buffer.writeln();
//
buffer.writeln(' ApiClient(this._dio) {');
buffer.writeln(' _initApis();');
buffer.writeln(' }');
buffer.writeln();
//
buffer.writeln(' void _initApis() {');
for (final versionEntry in apiClasses.entries) {
final version = versionEntry.key;
final versionUpper =
version == 'v1' ? '' : version.toUpperCase(); // v1不加后缀
for (final className in versionEntry.value) {
final fieldName = _toLowerCamelCase(className);
final suffix = version == 'v1' ? '' : versionUpper;
buffer.writeln(' _$fieldName$suffix = $className$suffix(_dio);');
}
}
buffer.writeln(' }');
buffer.writeln();
// 访
buffer.writeln(' // ========== 版本化 API 访问 ==========');
buffer.writeln();
for (final versionEntry in apiClasses.entries) {
final version = versionEntry.key;
final versionUpper =
version == 'v1' ? '' : version.toUpperCase(); // v1不加后缀
final versionLabel =
version == 'v1' ? 'V1默认版本' : '${version.toUpperCase()} 版本';
buffer.writeln(' /// $versionLabel API');
for (final className in versionEntry.value) {
final fieldName = _toLowerCamelCase(className);
final suffix = version == 'v1' ? '' : versionUpper;
buffer.writeln(
' $className$suffix get $fieldName$suffix => _$fieldName$suffix;');
}
buffer.writeln();
}
buffer.writeln('}');
return buffer.toString();
}
///
/// MobileManagerApi mobileManager
String _toLowerCamelCase(String className) {
// 'Api'
final name = className.replaceAll('Api', '');
//
return name[0].toLowerCase() + name.substring(1);
}
/// index.dart
Future<void> _generateVersionIndexFile(
String versionDir, List<String> fileNames) async {
final buffer = StringBuffer();
//
buffer.writeln('// API 接口导出文件');
buffer.writeln('// 由 xy_swagger_generator by max 生成');
buffer.writeln('// Copyright (C) 2025 YuanXuan. All rights reserved.');
buffer.writeln();
// API
final sortedFiles = fileNames.toList()..sort();
for (final fileName in sortedFiles) {
buffer.writeln('export \'$fileName\';');
}
final indexPath = '$versionDir/index.dart';
await FileUtils.writeFile(indexPath, buffer.toString());
}
} }
/// ///
class GenerateOptions { class GenerateOptions {
final bool generateEndpoints;
final bool generateModels; final bool generateModels;
final bool generateDocs; final bool generateDocs;
final bool generateApi; final bool generateApi;
@ -729,6 +330,7 @@ class GenerateOptions {
final bool splitByTags; final bool splitByTags;
const GenerateOptions({ const GenerateOptions({
required this.generateEndpoints,
required this.generateModels, required this.generateModels,
required this.generateDocs, required this.generateDocs,
required this.generateApi, required this.generateApi,

View File

@ -1,20 +1,9 @@
import 'config_loader.dart';
/// Swagger配置管理 /// Swagger配置管理
/// Swagger相关的配置项 /// Swagger相关的配置项
/// generator_config.yaml
class SwaggerConfig { class SwaggerConfig {
/// Swagger JSON URLs使 /// Swagger JSON文档的URL
static const List<String> defaultSwaggerJsonUrls = [ static const String swaggerJsonUrl =
// 'https://quanxue-test-api.w.23544.com:8843/swagger/v1/swagger.json', 'http://192.168.2.7:17288/swagger/v1/swagger.json';
'https://quanxue-test-api.w.23544.com:8843/swagger/v2/swagger.json',
];
/// Swagger JSON URLs
/// 使
static List<String> get swaggerJsonUrls {
return ConfigLoader.getSwaggerUrls();
}
/// API URL /// API URL
static const String baseUrl = 'http://192.168.2.7:17288'; static const String baseUrl = 'http://192.168.2.7:17288';
@ -31,14 +20,8 @@ class SwaggerConfig {
/// ///
static const String defaultModelsDir = 'api_models'; static const String defaultModelsDir = 'api_models';
/// ///
static String get generatorDir => ConfigLoader.getBaseDir(); static const String defaultEndpointsFile = 'generated_api_paths.dart';
/// API文件目录
static String get apiDir => ConfigLoader.getApiDir();
///
static String get modelsDir => ConfigLoader.getModelsDir();
/// ///
static const String defaultDocumentationFile = static const String defaultDocumentationFile =
@ -52,9 +35,9 @@ class SwaggerConfig {
/// ///
static const Map<String, dynamic> defaultGenerateOptions = { static const Map<String, dynamic> defaultGenerateOptions = {
'generateEndpoints': true,
'generateModels': true, 'generateModels': true,
'generateDocs': true, 'generateDocs': true,
'generateApi': true,
'useSimpleModels': false, 'useSimpleModels': false,
'separateModelFiles': true, 'separateModelFiles': true,
}; };

View File

@ -1,433 +0,0 @@
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import 'config.dart';
///
/// generator_config.yaml
class ConfigLoader {
static Map<String, dynamic>? _cachedConfig;
static String? _configPath;
/// YAML Dart Map
static Map<String, dynamic> _yamlToMap(dynamic yaml) {
if (yaml is YamlMap) {
final result = <String, dynamic>{};
yaml.forEach((key, value) {
final keyStr = key.toString();
if (value is YamlMap) {
result[keyStr] = _yamlToMap(value);
} else if (value is YamlList) {
result[keyStr] = _yamlToList(value);
} else {
result[keyStr] = value;
}
});
return result;
}
return {};
}
/// YAML Dart List
static List<dynamic> _yamlToList(YamlList yamlList) {
return yamlList.map((item) {
if (item is YamlMap) {
return _yamlToMap(item);
} else if (item is YamlList) {
return _yamlToList(item);
} else {
return item;
}
}).toList();
}
///
/// [configPath] generator_config.yaml
static Map<String, dynamic>? loadConfig([String? configPath]) {
// 使
if (_cachedConfig != null && configPath == _configPath) {
return _cachedConfig;
}
_configPath = configPath ?? _findConfigFile();
if (_configPath == null) {
return null;
}
try {
final file = File(_configPath!);
if (!file.existsSync()) {
return null;
}
final content = file.readAsStringSync();
final yaml = loadYaml(content);
_cachedConfig = _yamlToMap(yaml);
return _cachedConfig;
} catch (e) {
print('⚠️ 配置文件解析失败: $e');
return null;
}
}
///
/// generator_config.yaml
static String? _findConfigFile() {
var currentDir = Directory.current;
final maxDepth = 10; // 10
var depth = 0;
while (depth < maxDepth) {
final configFile =
File(path.join(currentDir.path, 'generator_config.yaml'));
if (configFile.existsSync()) {
return configFile.path;
}
final parent = currentDir.parent;
if (parent.path == currentDir.path) {
//
break;
}
currentDir = parent;
depth++;
}
return null;
}
///
static void clearCache() {
_cachedConfig = null;
_configPath = null;
}
/// Swagger URLs
/// swagger_urls ()
/// : ["url1", "url2"]
/// : [{url: "...", enabled: true}]
static List<String> getSwaggerUrls([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return SwaggerConfig.defaultSwaggerJsonUrls;
}
final input = cfg['input'] as Map<String, dynamic>?;
if (input == null) {
return SwaggerConfig.defaultSwaggerJsonUrls;
}
// swagger_urls ()
if (!input.containsKey('swagger_urls')) {
return SwaggerConfig.defaultSwaggerJsonUrls;
}
final urls = input['swagger_urls'];
if (urls is! List) {
return SwaggerConfig.defaultSwaggerJsonUrls;
}
final result = <String>[];
for (final item in urls) {
if (item is String) {
// : ["url1", "url2"]
result.add(item);
} else if (item is Map) {
// : [{url: "...", enabled: true}]
final enabled = item['enabled'] as bool? ?? true;
if (enabled) {
final url = item['url'] as String?;
if (url != null && url.isNotEmpty) {
result.add(url);
}
}
}
}
return result.isNotEmpty ? result : SwaggerConfig.defaultSwaggerJsonUrls;
}
///
///
static List<String> getIgnoredDirectories([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return [];
}
final output = cfg['output'] as Map<String, dynamic>?;
if (output == null) {
return [];
}
final ignoredDirs = output['ignored_directories'];
if (ignoredDirs is List) {
return ignoredDirs.map((item) => item.toString()).toList();
}
return [];
}
///
///
static List<String> getIgnoredFiles([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return [];
}
final output = cfg['output'] as Map<String, dynamic>?;
if (output == null) {
return [];
}
final ignoredFiles = output['ignored_files'];
if (ignoredFiles is List) {
return ignoredFiles.map((item) => item.toString()).toList();
}
return [];
}
///
///
static bool shouldSkipFile(String filePath, [Map<String, dynamic>? config]) {
// 1.
final ignoredDirs = getIgnoredDirectories(config);
if (ignoredDirs.isNotEmpty) {
// 使
final normalizedPath = filePath.replaceAll('\\', '/');
for (final ignoredDir in ignoredDirs) {
//
var normalizedDir = ignoredDir.toString().replaceAll('\\', '/');
//
if (normalizedDir.endsWith('/')) {
normalizedDir = normalizedDir.substring(0, normalizedDir.length - 1);
}
//
if (normalizedDir.isEmpty) {
continue;
}
//
//
if (normalizedPath.contains(normalizedDir)) {
//
final dirWithSlash = '$normalizedDir/';
if (normalizedPath.startsWith(dirWithSlash) ||
normalizedPath.contains('/$dirWithSlash') ||
normalizedPath == normalizedDir) {
return true;
}
}
}
}
// 2.
final ignoredFiles = getIgnoredFiles(config);
if (ignoredFiles.isNotEmpty) {
final fileName = path.basename(filePath);
for (final ignoredFile in ignoredFiles) {
final ignoredFileName = ignoredFile.toString();
//
if (fileName == ignoredFileName) {
return true;
}
//
if (ignoredFileName.startsWith('*')) {
// *user_api.dart user_api.dart
final suffix = ignoredFileName.substring(1);
if (fileName.endsWith(suffix)) {
return true;
}
} else if (ignoredFileName.endsWith('*')) {
// user_api.dart* user_api.dart
final prefix =
ignoredFileName.substring(0, ignoredFileName.length - 1);
if (fileName.startsWith(prefix)) {
return true;
}
}
//
if (ignoredFileName.contains('*') &&
ignoredFileName.startsWith('*') &&
ignoredFileName.endsWith('*')) {
// *user* user
final pattern =
ignoredFileName.substring(1, ignoredFileName.length - 1);
if (fileName.contains(pattern)) {
return true;
}
}
}
}
return false;
}
///
/// : {fileName}, {fileType}, {swaggerUrl}, {generatorName}, {author}, {copyright}
static String? getFileHeaderTemplate([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return null;
}
final templates = cfg['templates'] as Map<String, dynamic>?;
if (templates == null) {
return null;
}
return templates['file_header'] as String?;
}
///
static String getGeneratorName([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return 'xy_swagger_generator';
}
final generator = cfg['generator'] as Map<String, dynamic>?;
if (generator == null) {
return 'xy_swagger_generator';
}
return generator['name'] as String? ?? 'xy_swagger_generator';
}
///
static String getAuthor([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return 'max';
}
final generator = cfg['generator'] as Map<String, dynamic>?;
if (generator == null) {
return 'max';
}
return generator['author'] as String? ?? 'max';
}
///
static String getCopyright([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return 'Copyright (C) 2025 YuanXuan. All rights reserved.';
}
final generator = cfg['generator'] as Map<String, dynamic>?;
if (generator == null) {
return 'Copyright (C) 2025 YuanXuan. All rights reserved.';
}
return generator['copyright'] as String? ??
'Copyright (C) 2025 YuanXuan. All rights reserved.';
}
///
static String getBaseDir([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return SwaggerConfig.defaultGeneratorDir;
}
final output = cfg['output'] as Map<String, dynamic>?;
if (output == null) {
return SwaggerConfig.defaultGeneratorDir;
}
return output['base_dir'] as String? ?? SwaggerConfig.defaultGeneratorDir;
}
/// API
static String getApiDir([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return SwaggerConfig.defaultApiDir;
}
final output = cfg['output'] as Map<String, dynamic>?;
if (output == null) {
return SwaggerConfig.defaultApiDir;
}
return output['api_dir'] as String? ?? SwaggerConfig.defaultApiDir;
}
///
static String getModelsDir([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return SwaggerConfig.defaultModelsDir;
}
final output = cfg['output'] as Map<String, dynamic>?;
if (output == null) {
return SwaggerConfig.defaultModelsDir;
}
return output['models_dir'] as String? ?? SwaggerConfig.defaultModelsDir;
}
///
static String getVersionExtractionPattern([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return r'/api/v(\d+)/'; //
}
final generation = cfg['generation'] as Map<String, dynamic>?;
if (generation == null) {
return r'/api/v(\d+)/';
}
final api = generation['api'] as Map<String, dynamic>?;
if (api == null) {
return r'/api/v(\d+)/';
}
final versionExtraction =
api['version_extraction'] as Map<String, dynamic>?;
if (versionExtraction == null) {
return r'/api/v(\d+)/';
}
return versionExtraction['pattern'] as String? ?? r'/api/v(\d+)/';
}
///
static String getDefaultVersion([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return 'v1';
}
final generation = cfg['generation'] as Map<String, dynamic>?;
if (generation == null) {
return 'v1';
}
final api = generation['api'] as Map<String, dynamic>?;
if (api == null) {
return 'v1';
}
final versionExtraction =
api['version_extraction'] as Map<String, dynamic>?;
if (versionExtraction == null) {
return 'v1';
}
return versionExtraction['default_version'] as String? ?? 'v1';
}
}

View File

@ -39,26 +39,6 @@ enum HttpMethod {
} }
} }
///
/// API 使
enum ModelUsageType {
/// - requestBody parameters
/// defaultValue
request,
/// - response
/// defaultValue
response,
/// -
/// defaultValue
common,
/// - API 使
/// defaultValue
unknown,
}
/// API服务器信息 (OpenAPI 3.0) /// API服务器信息 (OpenAPI 3.0)
class ApiServer { class ApiServer {
/// URL /// URL
@ -1787,10 +1767,6 @@ class ApiModel {
/// (OpenAPI 3.0) /// (OpenAPI 3.0)
final ApiDiscriminator? discriminator; final ApiDiscriminator? discriminator;
///
/// API ///
final ModelUsageType usageType;
const ApiModel({ const ApiModel({
required this.name, required this.name,
required this.description, required this.description,
@ -1804,15 +1780,10 @@ class ApiModel {
this.anyOf = const [], this.anyOf = const [],
this.not, this.not,
this.discriminator, this.discriminator,
this.usageType = ModelUsageType.unknown,
}); });
/// JSON创建ApiModel /// JSON创建ApiModel
factory ApiModel.fromJson( factory ApiModel.fromJson(String name, Map<String, dynamic> json) {
String name,
Map<String, dynamic> json, {
ModelUsageType usageType = ModelUsageType.unknown,
}) {
final isEnum = json['enum'] != null; final isEnum = json['enum'] != null;
final enumValues = isEnum ? List<dynamic>.from(json['enum']) : <dynamic>[]; final enumValues = isEnum ? List<dynamic>.from(json['enum']) : <dynamic>[];
final properties = json['properties'] as Map<String, dynamic>? ?? {}; final properties = json['properties'] as Map<String, dynamic>? ?? {};
@ -1866,7 +1837,6 @@ class ApiModel {
anyOf: anyOf, anyOf: anyOf,
not: not, not: not,
discriminator: discriminator, discriminator: discriminator,
usageType: usageType,
properties: properties.map( properties: properties.map(
(propName, propData) => MapEntry( (propName, propData) => MapEntry(
propName, propName,
@ -1898,26 +1868,6 @@ class ApiModel {
/// ///
bool get hasDiscriminator => discriminator != null; bool get hasDiscriminator => discriminator != null;
///
/// schema 使
ApiModel copyWithUsageType(ModelUsageType newUsageType) {
return ApiModel(
name: name,
description: description,
properties: properties,
required: required,
isEnum: isEnum,
enumValues: enumValues,
enumType: enumType,
allOf: allOf,
oneOf: oneOf,
anyOf: anyOf,
not: not,
discriminator: discriminator,
usageType: newUsageType,
);
}
} }
/// API属性信息 /// API属性信息

View File

@ -13,16 +13,10 @@ abstract class BaseGenerator {
String generate(); String generate();
/// ///
/// [description] String generateFileHeader(String description) {
/// [fileName]
String generateFileHeader(String description, {String? fileName}) {
return StringUtils.generateFileHeader( return StringUtils.generateFileHeader(
description, description,
SwaggerConfig.swaggerJsonUrls.isNotEmpty SwaggerConfig.swaggerJsonUrl,
? SwaggerConfig.swaggerJsonUrls.first
: '',
fileName: fileName,
fileType: description,
); );
} }

View File

@ -85,15 +85,8 @@ class ModelCodeGenerator extends ModelGenerator {
// //
model.properties.forEach((propName, property) { model.properties.forEach((propName, property) {
final dartType = getDartPropertyType(property); final dartType = getDartPropertyType(property);
// // nullable: true
// 1. String date-time/date Swagger nullable final nullable = property.nullable ? '?' : '';
// 2. defaultValue json_annotation
// 3. nullable
final isNormalString = property.type == PropertyType.string &&
property.format != 'date-time' &&
property.format != 'date';
final hasDefaultValue = property.defaultValue != null || isNormalString;
final nullable = hasDefaultValue ? '' : (property.nullable ? '?' : '');
final dartPropName = StringUtils.toDartPropertyName(propName); final dartPropName = StringUtils.toDartPropertyName(propName);
if (property.description.isNotEmpty) { if (property.description.isNotEmpty) {
@ -104,7 +97,7 @@ class ModelCodeGenerator extends ModelGenerator {
// JsonKey注解 // JsonKey注解
final needsJsonKey = final needsJsonKey =
_needsJsonKeyAnnotation(dartPropName, propName, property, model); _needsJsonKeyAnnotation(dartPropName, propName, property);
if (needsJsonKey.isNotEmpty) { if (needsJsonKey.isNotEmpty) {
buffer.writeln(' @JsonKey($needsJsonKey)'); buffer.writeln(' @JsonKey($needsJsonKey)');
} }
@ -120,14 +113,8 @@ class ModelCodeGenerator extends ModelGenerator {
buffer.writeln(' const $className({'); buffer.writeln(' const $className({');
model.properties.forEach((propName, property) { model.properties.forEach((propName, property) {
final dartPropName = StringUtils.toDartPropertyName(propName); final dartPropName = StringUtils.toDartPropertyName(propName);
// required // required
// 1. String date-time/date required Swagger nullable final shouldBeRequired = !property.nullable;
// 2. required
// 3. required
final isNormalString = property.type == PropertyType.string &&
property.format != 'date-time' &&
property.format != 'date';
final shouldBeRequired = isNormalString || !property.nullable;
final required = shouldBeRequired ? 'required ' : ''; final required = shouldBeRequired ? 'required ' : '';
buffer.writeln(' ${required}this.$dartPropName,'); buffer.writeln(' ${required}this.$dartPropName,');
}); });
@ -152,35 +139,10 @@ class ModelCodeGenerator extends ModelGenerator {
return buffer.toString(); return buffer.toString();
} }
///
/// ///
String _getModelSubDirectory(ApiModel model) {
// enums
if (model.isEnum) {
return 'enums';
}
// usageType
switch (model.usageType) {
case ModelUsageType.request:
return 'request';
case ModelUsageType.response:
return 'result';
case ModelUsageType.common:
case ModelUsageType.unknown:
// common unknown result
//
return 'result';
}
}
/// ///
Map<String, String> generateSeparateModelFiles() { Map<String, String> generateSeparateModelFiles() {
final files = <String, String>{}; final files = <String, String>{};
//
final modelsByDirectory = <String, List<ApiModel>>{};
// //
for (final model in document.models.values) { for (final model in document.models.values) {
// total items // total items
@ -188,37 +150,28 @@ class ModelCodeGenerator extends ModelGenerator {
continue; // 使 BasePageResult<T> continue; // 使 BasePageResult<T>
} }
final subDir = _getModelSubDirectory(model);
modelsByDirectory.putIfAbsent(subDir, () => []).add(model);
final fileName = StringUtils.generateFileName(model.name); final fileName = StringUtils.generateFileName(model.name);
final filePath = '$subDir/$fileName'; final content = generateSingleModelFile(model);
final content = generateSingleModelFile(model, fileName: fileName); files[fileName] = content;
files[filePath] = content;
} }
// index.dart // index.dart
final indexContent = _generateMainIndexFile(modelsByDirectory); final modelFileNames = document.models.keys
.where((name) => !_isPaginationResponseModel(document.models[name]!))
.map((name) => StringUtils.generateFileName(name))
.toList();
final indexContent = generateIndexFile(modelFileNames);
files['index.dart'] = indexContent; files['index.dart'] = indexContent;
// index.dart
modelsByDirectory.forEach((subDir, models) {
final subIndexContent = _generateSubDirectoryIndexFile(models);
files['$subDir/index.dart'] = subIndexContent;
});
return files; return files;
} }
/// ///
String generateSingleModelFile(ApiModel model, {String? fileName}) { String generateSingleModelFile(ApiModel model) {
final buffer = StringBuffer(); final buffer = StringBuffer();
// //
buffer.writeln(generateFileHeader( buffer.writeln(generateFileHeader('${model.name} 模型定义'));
'${model.name} 模型定义',
fileName: fileName ?? StringUtils.generateFileName(model.name),
));
buffer.writeln(''); buffer.writeln('');
// json_annotation 使 @JsonEnum // json_annotation 使 @JsonEnum
@ -236,11 +189,10 @@ class ModelCodeGenerator extends ModelGenerator {
buffer.writeln(''); buffer.writeln('');
} }
// - 使 index.dart // - 使 index.dart
// result/user_result.dart '../index.dart'
final importedTypes = getImportedTypes(model); final importedTypes = getImportedTypes(model);
if (importedTypes.isNotEmpty) { if (importedTypes.isNotEmpty) {
buffer.writeln('import \'../index.dart\';'); buffer.writeln('import \'index.dart\';');
buffer.writeln(''); buffer.writeln('');
} }
@ -351,15 +303,8 @@ class ModelCodeGenerator extends ModelGenerator {
// //
model.properties.forEach((propName, property) { model.properties.forEach((propName, property) {
final dartType = getDartPropertyType(property); final dartType = getDartPropertyType(property);
// // nullable: true
// 1. String date-time/date Swagger nullable final nullable = property.nullable ? '?' : '';
// 2. defaultValue json_annotation
// 3. nullable
final isNormalString = property.type == PropertyType.string &&
property.format != 'date-time' &&
property.format != 'date';
final hasDefaultValue = property.defaultValue != null || isNormalString;
final nullable = hasDefaultValue ? '' : (property.nullable ? '?' : '');
final dartPropName = StringUtils.toDartPropertyName(propName); final dartPropName = StringUtils.toDartPropertyName(propName);
if (property.description.isNotEmpty) { if (property.description.isNotEmpty) {
@ -370,7 +315,7 @@ class ModelCodeGenerator extends ModelGenerator {
// JsonKey注解 // JsonKey注解
final needsJsonKey = final needsJsonKey =
_needsJsonKeyAnnotation(dartPropName, propName, property, model); _needsJsonKeyAnnotation(dartPropName, propName, property);
if (needsJsonKey.isNotEmpty) { if (needsJsonKey.isNotEmpty) {
buffer.writeln(' @JsonKey($needsJsonKey)'); buffer.writeln(' @JsonKey($needsJsonKey)');
} }
@ -386,14 +331,8 @@ class ModelCodeGenerator extends ModelGenerator {
buffer.writeln(' const $className({'); buffer.writeln(' const $className({');
model.properties.forEach((propName, property) { model.properties.forEach((propName, property) {
final dartPropName = StringUtils.toDartPropertyName(propName); final dartPropName = StringUtils.toDartPropertyName(propName);
// required // required
// 1. String date-time/date required Swagger nullable final shouldBeRequired = !property.nullable;
// 2. required
// 3. required
final isNormalString = property.type == PropertyType.string &&
property.format != 'date-time' &&
property.format != 'date';
final shouldBeRequired = isNormalString || !property.nullable;
final required = shouldBeRequired ? 'required ' : ''; final required = shouldBeRequired ? 'required ' : '';
buffer.writeln(' ${required}this.$dartPropName,'); buffer.writeln(' ${required}this.$dartPropName,');
}); });
@ -419,50 +358,6 @@ class ModelCodeGenerator extends ModelGenerator {
} }
/// ///
/// index.dart
String _generateMainIndexFile(Map<String, List<ApiModel>> modelsByDirectory) {
final buffer = StringBuffer();
buffer.writeln(generateFileHeader('API 模型导出文件'));
buffer.writeln('');
// library
buffer.writeln('library;');
buffer.writeln('');
// index.dart
final sortedDirs = modelsByDirectory.keys.toList()..sort();
for (final dir in sortedDirs) {
buffer.writeln('export \'$dir/index.dart\';');
}
return generateTypeCheckedCode(buffer.toString());
}
/// index.dart
String _generateSubDirectoryIndexFile(List<ApiModel> models) {
final buffer = StringBuffer();
buffer.writeln(generateFileHeader('模型导出文件'));
buffer.writeln('');
// library
buffer.writeln('library;');
buffer.writeln('');
//
final sortedModels = List<ApiModel>.from(models)
..sort((a, b) => a.name.compareTo(b.name));
for (final model in sortedModels) {
final fileName = StringUtils.generateFileName(model.name);
buffer.writeln('export \'$fileName\';');
}
return generateTypeCheckedCode(buffer.toString());
}
String generateIndexFile(List<String> modelFileNames) { String generateIndexFile(List<String> modelFileNames) {
final buffer = StringBuffer(); final buffer = StringBuffer();
@ -485,11 +380,7 @@ class ModelCodeGenerator extends ModelGenerator {
/// JsonKey注解以及注解的内容 /// JsonKey注解以及注解的内容
String _needsJsonKeyAnnotation( String _needsJsonKeyAnnotation(
String dartPropName, String dartPropName, String propName, ApiProperty property) {
String propName,
ApiProperty property,
ApiModel model,
) {
final annotations = <String>[]; final annotations = <String>[];
// JSON字段名不同时需要name参数 // JSON字段名不同时需要name参数
@ -497,36 +388,6 @@ class ModelCodeGenerator extends ModelGenerator {
annotations.add('name: \'$propName\''); annotations.add('name: \'$propName\'');
} }
// 使 usageType
// defaultValue
final isRequestModel = model.usageType == ModelUsageType.request;
// String类型默认值处理
//
if (!isRequestModel &&
property.type == PropertyType.string &&
property.format != 'date-time' &&
property.format != 'date') {
// String类型添加默认值为空字符串
if (property.defaultValue != null) {
// OpenAPI文档中有明确的默认值使
final defaultVal = property.defaultValue.toString();
annotations.add('defaultValue: \'$defaultVal\'');
} else {
// 使
annotations.add('defaultValue: \'\'');
}
}
// List类型默认值处理
// List添加默认值
//
if (!isRequestModel &&
property.type == PropertyType.array &&
!property.nullable) {
annotations.add('defaultValue: []');
}
// DateTime类型需要特殊处理 // DateTime类型需要特殊处理
if (property.type == PropertyType.string && if (property.type == PropertyType.string &&
(property.format == 'date-time' || property.format == 'date')) { (property.format == 'date-time' || property.format == 'date')) {
@ -534,20 +395,6 @@ class ModelCodeGenerator extends ModelGenerator {
// annotations.add('fromJson: DateTime.parse, toJson: _dateTimeToString'); // annotations.add('fromJson: DateTime.parse, toJson: _dateTimeToString');
} }
//
if (property.type != PropertyType.string && property.defaultValue != null) {
final defaultVal = property.defaultValue;
if (property.type == PropertyType.integer ||
property.type == PropertyType.number) {
annotations.add('defaultValue: $defaultVal');
} else if (property.type == PropertyType.boolean) {
annotations.add('defaultValue: $defaultVal');
} else {
//
annotations.add('defaultValue: \'$defaultVal\'');
}
}
// //
if (property.type == PropertyType.reference) { if (property.type == PropertyType.reference) {
// //

View File

@ -10,7 +10,6 @@ class RetrofitApiGenerator extends BaseGenerator {
final bool useDio; final bool useDio;
final bool splitByTags; final bool splitByTags;
final bool generateModels; final bool generateModels;
final bool versionedApi; // API
late SwaggerDocument document; late SwaggerDocument document;
@ -20,7 +19,6 @@ class RetrofitApiGenerator extends BaseGenerator {
this.useDio = true, this.useDio = true,
this.splitByTags = true, // this.splitByTags = true, //
this.generateModels = true, this.generateModels = true,
this.versionedApi = true, //
}); });
@override @override
@ -31,40 +29,6 @@ class RetrofitApiGenerator extends BaseGenerator {
throw UnimplementedError('Use generateFromDocument instead'); throw UnimplementedError('Use generateFromDocument instead');
} }
/// API
/// : /api/v1/User/GetData v1
/// /api/v2/User/GetData v2
/// /api/User/GetData v1 ()
String extractApiVersion(String path) {
final versionMatch = RegExp(r'/api/v(\d+)/').firstMatch(path);
if (versionMatch != null) {
return 'v${versionMatch.group(1)}';
}
return 'v1'; //
}
/// API paths
/// : Map<, Map<Tag, List<ApiPath>>>
Map<String, Map<String, List<ApiPath>>> groupApisByVersion(
List<ApiPath> paths,
) {
final versionGroups = <String, Map<String, List<ApiPath>>>{};
for (final path in paths) {
final version = extractApiVersion(path.path);
//
versionGroups.putIfAbsent(version, () => {});
// tag
for (final tag in path.tags) {
versionGroups[version]!.putIfAbsent(tag, () => []).add(path);
}
}
return versionGroups;
}
/// API /// API
String generateFromDocument(SwaggerDocument document) { String generateFromDocument(SwaggerDocument document) {
this.document = document; // this.document = document; //
@ -134,10 +98,8 @@ class RetrofitApiGenerator extends BaseGenerator {
final buffer = StringBuffer(); final buffer = StringBuffer();
final fileName = _generateTagFileName(tagName); //
buffer.writeln(generateFileHeader('$tagName API 接口定义'));
//
buffer.writeln(generateFileHeader('$tagName API 接口定义', fileName: fileName));
buffer.writeln(''); buffer.writeln('');
// //
@ -146,6 +108,7 @@ class RetrofitApiGenerator extends BaseGenerator {
// API // API
_generateTagApiInterface(buffer, tagName, paths); _generateTagApiInterface(buffer, tagName, paths);
final fileName = _generateTagFileName(tagName);
apiFiles[fileName] = generateTypeCheckedCode(buffer.toString()); apiFiles[fileName] = generateTypeCheckedCode(buffer.toString());
} }
@ -183,11 +146,17 @@ class RetrofitApiGenerator extends BaseGenerator {
'import \'package:learning_officer_oa/common/models/common/base_page_result.dart\';'); 'import \'package:learning_officer_oa/common/models/common/base_page_result.dart\';');
buffer.writeln(''); buffer.writeln('');
// index.dart //
// 使 index.dart final modelImports = _getRequiredModelImports();
// Dart tree-shaking 使 final sortedModelImports = modelImports.toList()..sort();
buffer.writeln('import \'../../api_models/index.dart\';'); for (final modelImport in sortedModelImports) {
buffer.writeln(
'import \'../../api_models/${StringUtils.generateFileName(modelImport)}\';');
}
if (modelImports.isNotEmpty) {
buffer.writeln(''); buffer.writeln('');
}
// part // part
buffer.writeln('part \'api_client.g.dart\';'); buffer.writeln('part \'api_client.g.dart\';');
@ -304,26 +273,17 @@ class RetrofitApiGenerator extends BaseGenerator {
buffer.writeln(' Future<$returnType> $methodName('); buffer.writeln(' Future<$returnType> $methodName(');
if (parameters.isNotEmpty) { if (parameters.isNotEmpty) {
// 使
buffer.writeln(' {');
for (int i = 0; i < parameters.length; i++) { for (int i = 0; i < parameters.length; i++) {
final param = parameters[i]; final param = parameters[i];
final isLast = i == parameters.length - 1; final isLast = i == parameters.length - 1;
// required
final requiredKeyword = param.required ? 'required ' : '';
if (param.annotation.isNotEmpty) { if (param.annotation.isNotEmpty) {
buffer.writeln( buffer.writeln(
' ${requiredKeyword}${param.annotation} ${param.type} ${param.name}${isLast ? '' : ','}'); ' ${param.annotation} ${param.type} ${param.name}${isLast ? '' : ','}');
} else { } else {
buffer.writeln( buffer.writeln(' ${param.type} ${param.name}${isLast ? '' : ','}');
' ${requiredKeyword}${param.type} ${param.name}${isLast ? '' : ','}');
} }
} }
buffer.writeln(' }');
} }
buffer.writeln(' );'); buffer.writeln(' );');
@ -1391,11 +1351,17 @@ class RetrofitApiGenerator extends BaseGenerator {
buffer.writeln(''); buffer.writeln('');
// index.dart //
// 使 index.dart final modelImports = _getRequiredModelImportsForPaths(paths);
// Dart tree-shaking 使 final sortedModelImports = modelImports.toList()..sort();
buffer.writeln('import \'../../api_models/index.dart\';'); for (final modelImport in sortedModelImports) {
buffer.writeln(
'import \'../../api_models/${StringUtils.generateFileName(modelImport)}\';');
}
if (modelImports.isNotEmpty) {
buffer.writeln(''); buffer.writeln('');
}
// part // part
final tagName = paths.first.tags.first; final tagName = paths.first.tags.first;
@ -1667,11 +1633,9 @@ class RetrofitApiGenerator extends BaseGenerator {
final buffer = StringBuffer(); final buffer = StringBuffer();
// //
buffer.writeln(generateFileHeader( buffer.writeln('// 参数实体类 - $className');
'参数实体类 - $className', buffer.writeln(
fileName: '${StringUtils.toSnakeCase(className)}.dart', '// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数');
));
buffer.writeln('// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数');
buffer.writeln(''); buffer.writeln('');
// //

View File

@ -18,34 +18,31 @@ class SwaggerDataParser {
final PerformanceMonitor _performanceMonitor; final PerformanceMonitor _performanceMonitor;
// //
final Map<String, SwaggerDocument> _cachedDocuments = {}; SwaggerDocument? _cachedDocument;
SwaggerDataParser() SwaggerDataParser()
: _cacheManager = CacheManager(), : _cacheManager = CacheManager(),
_performanceMonitor = PerformanceMonitor(); _performanceMonitor = PerformanceMonitor();
/// Swagger JSON文档 /// Swagger JSON文档
/// [url] 使 URL Future<SwaggerDocument> fetchAndParseSwaggerDocument() async {
Future<SwaggerDocument> fetchAndParseSwaggerDocument([String? url]) async { //
final swaggerUrl = url ?? SwaggerConfig.swaggerJsonUrls.first; if (_cachedDocument != null) {
return _cachedDocument!;
//
if (_cachedDocuments.containsKey(swaggerUrl)) {
print('📦 使用缓存的文档: $swaggerUrl');
return _cachedDocuments[swaggerUrl]!;
} }
return _performanceMonitor.measure( return _performanceMonitor.measure(
'fetchAndParseSwaggerDocument', 'fetchAndParseSwaggerDocument',
() async { () async {
try { try {
print('🔄 正在获取Swagger JSON文档: $swaggerUrl'); print('🔄 正在获取Swagger JSON文档...');
Map<String, dynamic> jsonData; Map<String, dynamic> jsonData;
if (swaggerUrl.startsWith('file://')) { if (SwaggerConfig.swaggerJsonUrl.startsWith('file://')) {
// //
final filePath = swaggerUrl.replaceFirst('file://', ''); final filePath =
SwaggerConfig.swaggerJsonUrl.replaceFirst('file://', '');
final file = File(filePath); final file = File(filePath);
if (await file.exists()) { if (await file.exists()) {
final content = await file.readAsString(); final content = await file.readAsString();
@ -53,14 +50,14 @@ class SwaggerDataParser {
} else { } else {
throw SwaggerParseException( throw SwaggerParseException(
'本地文件不存在', '本地文件不存在',
url: swaggerUrl, url: SwaggerConfig.swaggerJsonUrl,
details: '文件路径: $filePath', details: '文件路径: $filePath',
); );
} }
} else { } else {
// URL // URL
final response = await http.get( final response = await http.get(
Uri.parse(swaggerUrl), Uri.parse(SwaggerConfig.swaggerJsonUrl),
headers: SwaggerConfig.httpHeaders, headers: SwaggerConfig.httpHeaders,
); );
@ -69,24 +66,23 @@ class SwaggerDataParser {
} else { } else {
throw SwaggerParseException( throw SwaggerParseException(
'HTTP请求失败', 'HTTP请求失败',
url: swaggerUrl, url: SwaggerConfig.swaggerJsonUrl,
statusCode: response.statusCode, statusCode: response.statusCode,
details: 'HTTP响应状态码: ${response.statusCode}', details: 'HTTP响应状态码: ${response.statusCode}',
); );
} }
} }
final document = await parseSwaggerDocument(jsonData); _cachedDocument = await parseSwaggerDocument(jsonData);
_cachedDocuments[swaggerUrl] = document;
print('✅ Swagger文档解析完成'); print('✅ Swagger文档解析完成');
return document; return _cachedDocument!;
} catch (e) { } catch (e) {
if (e is SwaggerParseException) { if (e is SwaggerParseException) {
rethrow; rethrow;
} }
throw SwaggerParseException( throw SwaggerParseException(
'获取Swagger文档失败', '获取Swagger文档失败',
url: swaggerUrl, url: SwaggerConfig.swaggerJsonUrl,
details: e.toString(), details: e.toString(),
); );
} }
@ -103,8 +99,7 @@ class SwaggerDataParser {
final cacheKey = 'swagger_doc_${jsonData.hashCode}'; final cacheKey = 'swagger_doc_${jsonData.hashCode}';
final cachedResult = _cacheManager.get<SwaggerDocument>(cacheKey); final cachedResult = _cacheManager.get<SwaggerDocument>(cacheKey);
if (cachedResult != null) { if (cachedResult != null) {
// map 使 URL key _cachedDocument = cachedResult;
_cachedDocuments[SwaggerConfig.swaggerJsonUrls.first] = cachedResult;
return cachedResult; return cachedResult;
} }
@ -114,14 +109,11 @@ class SwaggerDataParser {
final version = info['version'] as String? ?? '1.0.0'; final version = info['version'] as String? ?? '1.0.0';
final description = info['description'] as String? ?? ''; final description = info['description'] as String? ?? '';
// schema 使 components
final schemaUsage = _analyzeSchemaUsage(jsonData);
// servers (OpenAPI 3.0) // servers (OpenAPI 3.0)
final servers = _parseServers(jsonData); final servers = _parseServers(jsonData);
// components (OpenAPI 3.0)使 // components (OpenAPI 3.0)
final components = _parseComponents(jsonData, schemaUsage); final components = _parseComponents(jsonData);
// tags信息 () // tags信息 ()
final tagsInfo = _parseTagsInfo(jsonData); final tagsInfo = _parseTagsInfo(jsonData);
@ -148,7 +140,7 @@ class SwaggerDataParser {
// //
_cacheManager.put(cacheKey, document); _cacheManager.put(cacheKey, document);
_cachedDocuments[SwaggerConfig.swaggerJsonUrls.first] = document; _cachedDocument = document;
return document; return document;
}); });
@ -183,10 +175,7 @@ class SwaggerDataParser {
} }
/// components (OpenAPI 3.0) /// components (OpenAPI 3.0)
ApiComponents _parseComponents( ApiComponents _parseComponents(Map<String, dynamic> jsonData) {
Map<String, dynamic> jsonData,
Map<String, Set<ModelUsageType>> schemaUsage,
) {
try { try {
final componentsJson = jsonData['components'] as Map<String, dynamic>?; final componentsJson = jsonData['components'] as Map<String, dynamic>?;
if (componentsJson != null) { if (componentsJson != null) {
@ -194,28 +183,10 @@ class SwaggerDataParser {
final resolver = ReferenceResolver(); final resolver = ReferenceResolver();
final resolvedSchemas = resolver.resolveModels(componentsJson); final resolvedSchemas = resolver.resolveModels(componentsJson);
// 使 usageType
final schemasWithUsageType = <String, ApiModel>{};
resolvedSchemas.forEach((name, model) {
final usages = schemaUsage[name] ?? {};
final ModelUsageType usageType;
if (usages.isEmpty) {
usageType = ModelUsageType.unknown;
} else if (usages.length == 1) {
usageType = usages.first;
} else {
//
usageType = ModelUsageType.common;
}
schemasWithUsageType[name] = model.copyWithUsageType(usageType);
});
// ApiComponents使 schemas // ApiComponents使 schemas
final components = ApiComponents.fromJson(componentsJson); final components = ApiComponents.fromJson(componentsJson);
return ApiComponents( return ApiComponents(
schemas: schemasWithUsageType, schemas: resolvedSchemas,
responses: components.responses, responses: components.responses,
parameters: components.parameters, parameters: components.parameters,
examples: components.examples, examples: components.examples,
@ -416,18 +387,17 @@ class SwaggerDataParser {
/// ///
void clearCache() { void clearCache() {
_cachedDocuments.clear(); _cachedDocument = null;
_cacheManager.clear(); _cacheManager.clear();
} }
/// ///
Map<String, dynamic> getDocumentStats() { Map<String, dynamic> getDocumentStats() {
if (_cachedDocuments.isEmpty) { if (_cachedDocument == null) {
return {}; return {};
} }
// 使 final doc = _cachedDocument!;
final doc = _cachedDocuments.values.first;
// HTTP方法 // HTTP方法
final methodStats = <String, int>{}; final methodStats = <String, int>{};
@ -456,132 +426,4 @@ class SwaggerDataParser {
'modelTypes': modelStats, 'modelTypes': modelStats,
}; };
} }
/// schemas API 使
/// schema
Map<String, Set<ModelUsageType>> _analyzeSchemaUsage(
Map<String, dynamic> jsonData,
) {
final schemaUsage = <String, Set<ModelUsageType>>{};
final pathsData = jsonData['paths'] as Map<String, dynamic>?;
if (pathsData == null) return schemaUsage;
pathsData.forEach((pathKey, pathValue) {
if (pathValue is! Map<String, dynamic>) return;
pathValue.forEach((methodKey, methodValue) {
if (methodValue is! Map<String, dynamic>) return;
// requestBody 使 schemas
final requestBody = methodValue['requestBody'] as Map<String, dynamic>?;
if (requestBody != null) {
_extractSchemasFromContent(
requestBody['content'] as Map<String, dynamic>?,
schemaUsage,
ModelUsageType.request,
);
}
// parameters 使 schemas
final parameters = methodValue['parameters'] as List<dynamic>?;
if (parameters != null) {
for (final param in parameters) {
if (param is Map<String, dynamic>) {
final schema = param['schema'] as Map<String, dynamic>?;
if (schema != null) {
_extractSchemaRef(schema, schemaUsage, ModelUsageType.request);
}
}
}
}
// responses 使 schemas
final responses = methodValue['responses'] as Map<String, dynamic>?;
if (responses != null) {
responses.forEach((statusCode, responseValue) {
if (responseValue is Map<String, dynamic>) {
_extractSchemasFromContent(
responseValue['content'] as Map<String, dynamic>?,
schemaUsage,
ModelUsageType.response,
);
}
});
}
});
});
return schemaUsage;
}
/// content schema
void _extractSchemasFromContent(
Map<String, dynamic>? content,
Map<String, Set<ModelUsageType>> schemaUsage,
ModelUsageType usageType,
) {
if (content == null) return;
content.forEach((mediaType, mediaTypeValue) {
if (mediaTypeValue is Map<String, dynamic>) {
final schema = mediaTypeValue['schema'] as Map<String, dynamic>?;
if (schema != null) {
_extractSchemaRef(schema, schemaUsage, usageType);
}
}
});
}
/// schema $ref
void _extractSchemaRef(
Map<String, dynamic> schema,
Map<String, Set<ModelUsageType>> schemaUsage,
ModelUsageType usageType,
) {
// $ref
final ref = schema['\$ref'] as String?;
if (ref != null) {
// #/components/schemas/ModelName ModelName
final schemaName = ref.split('/').last;
schemaUsage.putIfAbsent(schemaName, () => {}).add(usageType);
return; // $ref
}
//
if (schema['type'] == 'array') {
final items = schema['items'] as Map<String, dynamic>?;
if (items != null) {
_extractSchemaRef(items, schemaUsage, usageType);
}
}
// allOf/oneOf/anyOf
for (final key in ['allOf', 'oneOf', 'anyOf']) {
final schemas = schema[key] as List<dynamic>?;
if (schemas != null) {
for (final s in schemas) {
if (s is Map<String, dynamic>) {
_extractSchemaRef(s, schemaUsage, usageType);
}
}
}
}
//
final properties = schema['properties'] as Map<String, dynamic>?;
if (properties != null) {
properties.forEach((propName, propSchema) {
if (propSchema is Map<String, dynamic>) {
_extractSchemaRef(propSchema, schemaUsage, usageType);
}
});
}
// additionalProperties
final additionalProperties = schema['additionalProperties'];
if (additionalProperties is Map<String, dynamic>) {
_extractSchemaRef(additionalProperties, schemaUsage, usageType);
}
}
} }

View File

@ -153,7 +153,7 @@ class SwaggerCLI {
/// ///
void _showContact() { void _showContact() {
print('🌐 更多信息:'); print('🌐 更多信息:');
print(' API文档: ${SwaggerConfig.swaggerJsonUrls.first}'); print(' API文档: ${SwaggerConfig.swaggerJsonUrl}');
print(' 基础URL: ${SwaggerConfig.baseUrl}'); print(' 基础URL: ${SwaggerConfig.baseUrl}');
print(''); print('');
} }

View File

@ -19,7 +19,6 @@
/// ///
library; library;
import '../core/config_loader.dart';
import '../core/models.dart'; import '../core/models.dart';
class StringUtils { class StringUtils {
@ -353,73 +352,12 @@ class StringUtils {
} }
/// ///
/// [description] static String generateFileHeader(String description, String source) {
/// [source] Swagger URL // final timestamp = DateTime.now().toLocal().toString().split(" ").first;
/// [fileName]
/// [fileType] "API 接口定义""模型定义"
static String generateFileHeader(
String description,
String source, {
String? fileName,
String? fileType,
}) {
//
final template = ConfigLoader.getFileHeaderTemplate();
if (template != null && template.isNotEmpty) {
// 使
return _applyFileHeaderTemplate(
template,
description: description,
source: source,
fileName: fileName ?? '',
fileType: fileType ?? description,
generatorName: ConfigLoader.getGeneratorName(),
author: ConfigLoader.getAuthor(),
copyright: ConfigLoader.getCopyright(),
);
}
// 使
final generatorName = ConfigLoader.getGeneratorName();
final author = ConfigLoader.getAuthor();
final copyright = ConfigLoader.getCopyright();
return '''// $description return '''// $description
// Swagger API : $source // Swagger API :
// $generatorName by $author // xy_swagger_generator by max
// $copyright // Copyright (C) 2025 YuanXuan. All rights reserved.
'''; ''';
} }
///
/// : {fileName}, {fileType}, {swaggerUrl}, {generatorName}, {author}, {copyright}
static String _applyFileHeaderTemplate(
String template, {
required String description,
required String source,
required String fileName,
required String fileType,
required String generatorName,
required String author,
required String copyright,
}) {
var result = template;
//
result = result.replaceAll('{fileName}', fileName);
result = result.replaceAll('{fileType}', fileType);
result = result.replaceAll('{swaggerUrl}', source);
result = result.replaceAll('{generatorName}', generatorName);
result = result.replaceAll('{author}', author);
result = result.replaceAll('{copyright}', copyright);
// 使
if (!result.contains('//')) {
//
result = '// $description\n$result';
}
return result;
}
} }

View File

@ -249,6 +249,17 @@ class TypeValidator {
); );
} }
break; break;
case CodeType.endpoints:
if (!code.contains('class ')) {
errors.add(
const ValidationError(
field: 'content',
message: '端点代码必须包含class声明',
severity: ErrorSeverity.error,
),
);
}
break;
case CodeType.documentation: case CodeType.documentation:
if (!code.contains('#')) { if (!code.contains('#')) {
warnings.add('文档代码似乎不包含Markdown标题'); warnings.add('文档代码似乎不包含Markdown标题');
@ -618,4 +629,4 @@ class ValidationError {
enum ErrorSeverity { warning, error, critical } enum ErrorSeverity { warning, error, critical }
/// ///
enum CodeType { model, documentation } enum CodeType { model, endpoints, documentation }

View File

@ -5,18 +5,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _fe_analyzer_shared name: _fe_analyzer_shared
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "67.0.0" version: "85.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" sha256: de617bfdc64f3d8b00835ec2957441ceca0a29cdf7881f7ab231bc14f71159c0
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.4.1" version: "7.5.6"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -45,10 +45,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.1" version: "2.5.4"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
@ -69,26 +69,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_resolvers name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.2" version: "2.5.4"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.13" version: "2.5.4"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "7.3.2" version: "9.1.2"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@ -105,6 +105,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "8.10.1" version: "8.10.1"
characters:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
checked_yaml: checked_yaml:
dependency: transitive dependency: transitive
description: description:
@ -121,6 +129,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
@ -165,12 +181,12 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dart_style name: dart_style
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.3.6" version: "3.1.0"
dio: dio:
dependency: "direct main" dependency: "direct dev"
description: description:
name: dio name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
@ -185,6 +201,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -201,6 +225,16 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:
@ -277,10 +311,34 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: json_serializable name: json_serializable
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.8.0" version: "6.9.5"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.9"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
logging: logging:
dependency: "direct main" dependency: "direct main"
description: description:
@ -297,6 +355,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.12.17" version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -345,14 +411,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
protobuf:
dependency: transitive
description:
name: protobuf
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@ -370,21 +428,13 @@ packages:
source: hosted source: hosted
version: "1.5.0" version: "1.5.0"
retrofit: retrofit:
dependency: "direct main" dependency: "direct dev"
description: description:
name: retrofit name: retrofit
sha256: "84d70114a5b6bae5f4c1302335f9cb610ebeb1b02023d5e7e87697aaff52926a" sha256: "84d70114a5b6bae5f4c1302335f9cb610ebeb1b02023d5e7e87697aaff52926a"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.6.0" version: "4.6.0"
retrofit_generator:
dependency: "direct dev"
description:
name: retrofit_generator
sha256: "8dfc406cdfa171f33cbd21bf5bd8b6763548cc217de19cdeaa07a76727fac4ca"
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.2.1"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -413,26 +463,31 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shelf_web_socket name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.1" version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_gen: source_gen:
dependency: transitive dependency: transitive
description: description:
name: source_gen name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.5.0" version: "2.0.0"
source_helper: source_helper:
dependency: transitive dependency: transitive
description: description:
name: source_helper name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" sha256: "4f81479fe5194a622cdd1713fe1ecb683a6e6c85cd8cec8e2e35ee5ab3fdf2a1"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.5" version: "1.3.6"
source_map_stack_trace: source_map_stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -501,26 +556,26 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: test name: test
sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.26.2" version: "1.25.15"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.7.6" version: "0.7.4"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.6.11" version: "0.6.8"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@ -529,14 +584,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
tuple:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -545,6 +592,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
@ -594,7 +649,7 @@ packages:
source: hosted source: hosted
version: "1.2.1" version: "1.2.1"
yaml: yaml:
dependency: "direct main" dependency: transitive
description: description:
name: yaml name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
@ -603,3 +658,4 @@ packages:
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.8.0 <4.0.0" dart: ">=3.8.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@ -1,41 +1,32 @@
name: swagger_generator_flutter name: swagger_generator_flutter
description: A powerful Swagger/OpenAPI code generator for Flutter projects with Dio + Retrofit support description: A Flutter project using generated API models
version: 2.1.1 version: 2.0.1+3
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
# 可执行命令配置
# 默认入口bin/swagger_generator_flutter.dart
# 使用方式dart run swagger_generator_flutter generate --all
# 或者使用别名dart run swagger_generator_flutter:swagger_generator
executables:
swagger_generator: main
swagger_generator_flutter: swagger_generator_flutter
dependencies: dependencies:
# Flutter SDK可选仅当需要 Flutter 特性时) flutter:
# 注释掉以支持纯 Dart 项目 sdk: flutter
# flutter:
# sdk: flutter
# 核心依赖 # 推荐版本必需的依赖
path: ^1.8.0
logging: ^1.1.0
yaml: ^3.1.0
# HTTP 和 API 相关(仅用于类型引用,不是运行时依赖)
http: ^1.1.0
dio: ^5.0.0
retrofit: ^4.0.0
json_annotation: ^4.8.1 json_annotation: ^4.8.1
http: ^1.1.0
path: any
logging: any
dev_dependencies: dev_dependencies:
# 测试框架 flutter_test:
test: ^1.24.0 sdk: flutter
# 代码生成工具(仅用于测试/示例) # 代码生成必需的依赖
build_runner: ^2.4.7 build_runner: ^2.4.7
json_serializable: ^6.7.1 json_serializable: ^6.7.1
retrofit_generator: ^8.0.0 test: ^1.24.0
dio: any
retrofit: any
learning_officer_oa: any
flutter:
uses-material-design: true

12557
swagger.json

File diff suppressed because it is too large Load Diff