341 lines
11 KiB
Dart
341 lines
11 KiB
Dart
import 'dart:io';
|
||
|
||
import 'package:path/path.dart' as path;
|
||
|
||
import '../core/models.dart';
|
||
import '../generators/documentation_generator.dart';
|
||
import '../generators/endpoint_code_generator.dart';
|
||
import '../generators/model_code_generator.dart';
|
||
import '../generators/retrofit_api_generator.dart';
|
||
import '../parsers/swagger_data_parser.dart';
|
||
import '../utils/file_utils.dart';
|
||
import 'base_command.dart';
|
||
|
||
/// Generate命令
|
||
/// 用于生成各种代码文件
|
||
class GenerateCommand extends BaseCommand {
|
||
@override
|
||
String get name => 'generate';
|
||
|
||
@override
|
||
String get description => '生成API代码文件(模型、端点、文档等)';
|
||
|
||
@override
|
||
String get usage => 'dart swagger_cli.dart generate [options]';
|
||
|
||
@override
|
||
List<CommandOption> get options => [
|
||
const CommandOption(
|
||
name: 'endpoints',
|
||
shortName: 'e',
|
||
description: '生成API端点常量',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'models',
|
||
shortName: 'm',
|
||
description: '生成数据模型',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'docs',
|
||
shortName: 'd',
|
||
description: '生成API文档',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'api',
|
||
shortName: 'r',
|
||
description: '生成Retrofit风格API接口',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'split-by-tags',
|
||
shortName: 't',
|
||
description: '按tags分组生成多个API文件(默认启用)',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'all',
|
||
shortName: 'a',
|
||
description: '生成所有文件(默认)',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'simple',
|
||
shortName: 's',
|
||
description: '使用简洁版模型生成',
|
||
type: OptionType.flag,
|
||
),
|
||
const CommandOption(
|
||
name: 'output-dir',
|
||
shortName: 'o',
|
||
description: '输出目录',
|
||
type: OptionType.string,
|
||
defaultValue: 'generator',
|
||
),
|
||
];
|
||
|
||
@override
|
||
Future<int> execute(List<String> args) async {
|
||
try {
|
||
final parsedArgs = parseArguments(args);
|
||
validateArguments(parsedArgs);
|
||
|
||
await prepare(parsedArgs);
|
||
|
||
// 解析Swagger文档
|
||
progress('正在解析Swagger文档...');
|
||
final parser = SwaggerDataParser();
|
||
final document = await parser.fetchAndParseSwaggerDocument();
|
||
|
||
// 解析生成选项
|
||
final options = _parseGenerateOptions(parsedArgs);
|
||
final fullOutputDir = FileUtils.getProjectRootGeneratorDir();
|
||
|
||
progress('输出目录: $fullOutputDir');
|
||
|
||
// 确保输出目录存在
|
||
await FileUtils.ensureDirectoryExists(fullOutputDir);
|
||
|
||
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) {
|
||
progress('正在生成数据模型...');
|
||
final generator = ModelCodeGenerator(
|
||
document,
|
||
useSimpleModels: options.useSimpleModels,
|
||
);
|
||
|
||
final modelsDir = '$fullOutputDir/api_models';
|
||
await FileUtils.ensureDirectoryExists(modelsDir);
|
||
|
||
final modelFiles = generator.generateSeparateModelFiles();
|
||
|
||
for (final entry in modelFiles.entries) {
|
||
final filePath = '$modelsDir/${entry.key}';
|
||
await FileUtils.writeFile(filePath, entry.value);
|
||
success('模型文件已保存到: $filePath');
|
||
generatedFiles++;
|
||
}
|
||
}
|
||
|
||
// 生成 Retrofit 风格的 API 接口(默认使用拆分模式)
|
||
if (options.generateApi) {
|
||
progress('正在按tags分组生成Retrofit风格API接口...');
|
||
final generator = RetrofitApiGenerator(
|
||
className: 'ApiClient',
|
||
useRetrofit: true,
|
||
useDio: true,
|
||
splitByTags: true, // 强制使用拆分模式
|
||
);
|
||
|
||
// 设置文档
|
||
generator.document = document;
|
||
|
||
// 确保参数实体类已生成
|
||
generator.ensureParameterEntitiesGenerated();
|
||
|
||
// 生成按 tags 分组的多个 API 文件
|
||
final tagApiFiles = generator.generateApiFilesByTags();
|
||
|
||
final apiDir = '$fullOutputDir/api';
|
||
await FileUtils.ensureDirectoryExists(apiDir);
|
||
|
||
for (final entry in tagApiFiles.entries) {
|
||
final fileName = entry.key;
|
||
final code = entry.value;
|
||
final filePath = '$apiDir/$fileName';
|
||
await FileUtils.writeFile(filePath, code);
|
||
success('API接口文件已保存到: $filePath');
|
||
generatedFiles++;
|
||
}
|
||
|
||
// 生成主 API 文件
|
||
final mainCode = generator.generateMainApiFile();
|
||
final mainFilePath = '$apiDir/api_client.dart';
|
||
await FileUtils.writeFile(mainFilePath, mainCode);
|
||
success('主API接口文件已保存到: $mainFilePath');
|
||
generatedFiles++;
|
||
|
||
// 生成参数实体类文件
|
||
final parameterEntityFiles = generator.generateParameterEntityFiles();
|
||
if (parameterEntityFiles.isNotEmpty) {
|
||
final modelsDir = '$fullOutputDir/api_models';
|
||
await FileUtils.ensureDirectoryExists(modelsDir);
|
||
|
||
for (final entry in parameterEntityFiles.entries) {
|
||
final filePath = '$modelsDir/${entry.key}';
|
||
await FileUtils.writeFile(filePath, entry.value);
|
||
success('参数实体类文件已保存到: $filePath');
|
||
generatedFiles++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 重新生成 index.dart 文件,包含所有生成的文件
|
||
if (options.generateModels || options.generateApi) {
|
||
progress('正在重新生成 index.dart 文件...');
|
||
final modelsDir = '$fullOutputDir/api_models';
|
||
final allFiles = await _getAllModelFiles(modelsDir);
|
||
final indexContent = _generateUpdatedIndexFile(allFiles);
|
||
final indexPath = '$modelsDir/index.dart';
|
||
await FileUtils.writeFile(indexPath, indexContent);
|
||
success('index.dart 文件已更新');
|
||
}
|
||
|
||
// 生成文档
|
||
if (options.generateDocs) {
|
||
progress('正在生成API文档...');
|
||
final generator = DocumentationGenerator(document);
|
||
final code = generator.generate();
|
||
|
||
final filePath = '$fullOutputDir/api_documentation.md';
|
||
await FileUtils.writeFile(filePath, code);
|
||
success('API文档已保存到: $filePath');
|
||
generatedFiles++;
|
||
}
|
||
|
||
// 生成摘要
|
||
_generateSummary(document, fullOutputDir);
|
||
|
||
success('代码生成完成!共生成 $generatedFiles 个文件');
|
||
return 0;
|
||
} catch (e) {
|
||
print('❌ 生成失败: $e');
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/// 解析生成选项
|
||
GenerateOptions _parseGenerateOptions(ParsedArguments args) {
|
||
final hasAnyFlag = args.hasOption('endpoints') ||
|
||
args.hasOption('models') ||
|
||
args.hasOption('docs') ||
|
||
args.hasOption('api');
|
||
|
||
return GenerateOptions(
|
||
generateEndpoints: hasAnyFlag
|
||
? (args.getOption<bool>('endpoints') ?? false)
|
||
: (args.getOption<bool>('all') ?? true),
|
||
generateModels: hasAnyFlag
|
||
? (args.getOption<bool>('models') ?? false)
|
||
: (args.getOption<bool>('all') ?? true),
|
||
generateDocs: hasAnyFlag
|
||
? (args.getOption<bool>('docs') ?? false)
|
||
: (args.getOption<bool>('all') ?? true),
|
||
generateApi: hasAnyFlag
|
||
? (args.getOption<bool>('api') ?? false)
|
||
: (args.getOption<bool>('all') ?? true),
|
||
useSimpleModels: args.getOption<bool>('simple') ?? false,
|
||
splitByTags: args.getOption<bool>('split-by-tags') ?? true, // 默认启用拆分模式
|
||
);
|
||
}
|
||
|
||
/// 获取所有模型文件列表
|
||
Future<List<String>> _getAllModelFiles(String modelsDir) async {
|
||
try {
|
||
final directory = Directory(modelsDir);
|
||
if (!await directory.exists()) {
|
||
return [];
|
||
}
|
||
|
||
final files = await directory.list().toList();
|
||
final dartFiles = <String>[];
|
||
|
||
for (final entity in files) {
|
||
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')) {
|
||
dartFiles.add(fileName);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 按文件名排序
|
||
dartFiles.sort();
|
||
return dartFiles;
|
||
} catch (e) {
|
||
print('获取模型文件列表失败: $e');
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/// 生成更新的 index.dart 文件内容
|
||
String _generateUpdatedIndexFile(List<String> fileNames) {
|
||
final buffer = StringBuffer();
|
||
|
||
// 生成文件头
|
||
buffer.writeln('// API 模型导出文件');
|
||
buffer.writeln('// 基于 Swagger API 文档: ');
|
||
buffer.writeln('// 由 xy_swagger_generator by max 生成');
|
||
buffer.writeln('// Copyright (C) 2025 YuanXuan. All rights reserved.');
|
||
buffer.writeln('');
|
||
buffer.writeln('library;');
|
||
buffer.writeln('');
|
||
|
||
// 导出所有文件
|
||
for (final fileName in fileNames) {
|
||
buffer.writeln('export \'$fileName\';');
|
||
}
|
||
|
||
return buffer.toString();
|
||
}
|
||
|
||
/// 生成摘要信息
|
||
void _generateSummary(SwaggerDocument document, String outputDir) {
|
||
final summary = StringBuffer();
|
||
summary.writeln('# 代码生成摘要');
|
||
summary.writeln('');
|
||
summary.writeln('**API标题**: ${document.title}');
|
||
summary.writeln('**API版本**: ${document.version}');
|
||
summary.writeln('**生成时间**: ${DateTime.now().toIso8601String()}');
|
||
summary.writeln('');
|
||
summary.writeln('## 统计信息');
|
||
summary.writeln('- 控制器数量: ${document.controllers.length}');
|
||
summary.writeln('- API路径数量: ${document.paths.length}');
|
||
summary.writeln('- 数据模型数量: ${document.models.length}');
|
||
summary.writeln('');
|
||
summary.writeln('## 控制器列表');
|
||
document.controllers.forEach((name, controller) {
|
||
summary.writeln(
|
||
'- **$name**: ${controller.description} (${controller.paths.length} 个路径)');
|
||
});
|
||
|
||
FileUtils.writeFile('$outputDir/SUMMARY.md', summary.toString());
|
||
}
|
||
}
|
||
|
||
/// 生成选项
|
||
class GenerateOptions {
|
||
final bool generateEndpoints;
|
||
final bool generateModels;
|
||
final bool generateDocs;
|
||
final bool generateApi;
|
||
final bool useSimpleModels;
|
||
final bool splitByTags;
|
||
|
||
const GenerateOptions({
|
||
required this.generateEndpoints,
|
||
required this.generateModels,
|
||
required this.generateDocs,
|
||
required this.generateApi,
|
||
required this.useSimpleModels,
|
||
required this.splitByTags,
|
||
});
|
||
}
|