Merge branch 'release/2.1.2'

This commit is contained in:
Max 2025-11-05 18:38:57 +08:00
commit c1a6c94357
10 changed files with 20759 additions and 147 deletions

View File

@ -11,6 +11,9 @@ generator:
# 输入配置 # 输入配置
input: input:
# Swagger 文档源(支持多版本) # Swagger 文档源(支持多版本)
# 注意:多个 URL 会按顺序合并,后面的文档会覆盖前面的同名模型和路径
# 因此建议将高版本(如 V2配置在低版本如 V1之后以确保高版本的模型覆盖低版本
# 例如V1 在前V2 在后,那么 V2 的模型会覆盖 V1 的同名模型
swagger_urls: # 完整形式:可以控制每个版本的启用状态 swagger_urls: # 完整形式:可以控制每个版本的启用状态
- url: "https://quanxue-test-api.w.23544.com:8843/swagger/v1/swagger.json" - url: "https://quanxue-test-api.w.23544.com:8843/swagger/v1/swagger.json"
enabled: true enabled: true

File diff suppressed because it is too large Load Diff

View File

@ -84,27 +84,64 @@ class GenerateCommand extends BaseCommand {
final parser = SwaggerDataParser(); final parser = SwaggerDataParser();
// paths models // paths models
//
// V2 V1
SwaggerDocument? mergedDocument; SwaggerDocument? mergedDocument;
for (int i = 0; i < SwaggerConfig.swaggerJsonUrls.length; i++) { final urls = SwaggerConfig.swaggerJsonUrls;
final url = SwaggerConfig.swaggerJsonUrls[i]; progress('URL 处理顺序: ${urls.join(" -> ")}');
progress(
' [${i + 1}/${SwaggerConfig.swaggerJsonUrls.length}] 正在解析: $url'); for (int i = 0; i < urls.length; i++) {
final url = urls[i];
progress(' [${i + 1}/${urls.length}] 正在解析: $url');
final doc = await parser.fetchAndParseSwaggerDocument(url); final doc = await parser.fetchAndParseSwaggerDocument(url);
progress(' 解析完成: ${doc.models.length} 个模型, ${doc.paths.length} 个路径');
if (mergedDocument == null) { if (mergedDocument == null) {
mergedDocument = doc; mergedDocument = doc;
progress(' 初始文档: ${doc.models.length} 个模型');
} else { } else {
// paths models // paths models
// 使 {...mergedDocument.models, ...doc.models}
// doc.models mergedDocument.models key
//
final beforeModelCount = mergedDocument.models.length;
final currentModelCount = doc.models.length;
//
final overlappingModels = <String>[];
for (final key in doc.models.keys) {
if (mergedDocument.models.containsKey(key)) {
overlappingModels.add(key);
}
}
if (overlappingModels.isNotEmpty) {
progress(
' 发现 ${overlappingModels.length} 个同名模型将被覆盖: ${overlappingModels.take(5).join(", ")}${overlappingModels.length > 5 ? "..." : ""}');
}
mergedDocument = SwaggerDocument( mergedDocument = SwaggerDocument(
title: mergedDocument.title, title: mergedDocument.title,
description: mergedDocument.description, description: mergedDocument.description,
version: '${mergedDocument.version} + ${doc.version}', version: '${mergedDocument.version} + ${doc.version}',
// doc mergedDocument key
// V2 V1
paths: {...mergedDocument.paths, ...doc.paths}, paths: {...mergedDocument.paths, ...doc.paths},
models: {...mergedDocument.models, ...doc.models}, models: {...mergedDocument.models, ...doc.models},
controllers: {...mergedDocument.controllers, ...doc.controllers}, controllers: {...mergedDocument.controllers, ...doc.controllers},
); );
final afterModelCount = mergedDocument.models.length;
progress(
' 合并后: $beforeModelCount + $currentModelCount -> $afterModelCount 个模型');
//
if (overlappingModels.isNotEmpty) {
progress(
' 同名模型列表: ${overlappingModels.take(10).join(", ")}${overlappingModels.length > 10 ? "..." : ""}');
}
} }
} }
@ -118,12 +155,20 @@ class GenerateCommand extends BaseCommand {
// //
final options = _parseGenerateOptions(parsedArgs); final options = _parseGenerateOptions(parsedArgs);
final fullOutputDir = FileUtils.getProjectRootGeneratorDir();
progress('输出目录: $fullOutputDir'); // 使
final baseDir = SwaggerConfig.generatorDir;
final apiDir = SwaggerConfig.apiDir;
final modelsDir = SwaggerConfig.modelsDir;
progress('输出目录: $baseDir');
progress('API 目录: $apiDir');
progress('模型目录: $modelsDir');
// //
await FileUtils.ensureDirectoryExists(fullOutputDir); await FileUtils.ensureDirectoryExists(baseDir);
await FileUtils.ensureDirectoryExists(apiDir);
await FileUtils.ensureDirectoryExists(modelsDir);
int generatedFiles = 0; int generatedFiles = 0;
@ -135,20 +180,19 @@ class GenerateCommand extends BaseCommand {
useSimpleModels: options.useSimpleModels, useSimpleModels: options.useSimpleModels,
); );
final modelsDir = '$fullOutputDir/api_models';
await FileUtils.ensureDirectoryExists(modelsDir); await FileUtils.ensureDirectoryExists(modelsDir);
final modelFiles = generator.generateSeparateModelFiles(); final modelFiles = generator.generateSeparateModelFiles();
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)) { if (ConfigLoader.shouldSkipFile(filePath)) {
progress('跳过文件: $filePath'); progress('跳过文件: $filePath');
continue; continue;
} }
await FileUtils.writeFile(filePath, entry.value); await FileUtils.writeFile(filePath, entry.value);
success('模型文件已保存到: $filePath'); success('模型文件已保存到: $filePath');
generatedFiles++; generatedFiles++;
@ -159,7 +203,6 @@ class GenerateCommand extends BaseCommand {
if (options.generateApi) { if (options.generateApi) {
progress('正在按版本和tags分组生成Retrofit风格API接口...'); progress('正在按版本和tags分组生成Retrofit风格API接口...');
final apiDir = '$fullOutputDir/api';
await FileUtils.ensureDirectoryExists(apiDir); await FileUtils.ensureDirectoryExists(apiDir);
// 🎯 paths // 🎯 paths
@ -224,26 +267,26 @@ class GenerateCommand extends BaseCommand {
final files = versionEntry.value; final files = versionEntry.value;
final versionDir = '$apiDir/$version'; final versionDir = '$apiDir/$version';
// //
if (ConfigLoader.shouldSkipFile(versionDir)) { if (ConfigLoader.shouldSkipFile(versionDir)) {
progress('跳过版本目录: $versionDir'); progress('跳过版本目录: $versionDir');
continue; continue;
} }
await FileUtils.ensureDirectoryExists(versionDir); await FileUtils.ensureDirectoryExists(versionDir);
for (final fileEntry in files.entries) { for (final fileEntry in files.entries) {
final fileName = fileEntry.key; final fileName = fileEntry.key;
final code = fileEntry.value; final code = fileEntry.value;
final filePath = '$versionDir/$fileName'; final filePath = '$versionDir/$fileName';
// //
if (ConfigLoader.shouldSkipFile(filePath)) { if (ConfigLoader.shouldSkipFile(filePath)) {
progress('跳过文件: $filePath'); progress('跳过文件: $filePath');
continue; continue;
} }
await FileUtils.writeFile(filePath, code); await FileUtils.writeFile(filePath, code);
success('API接口文件已保存到: $filePath'); success('API接口文件已保存到: $filePath');
generatedFiles++; generatedFiles++;
@ -259,7 +302,7 @@ class GenerateCommand extends BaseCommand {
// API ApiClient // API ApiClient
final mainCode = _generateVersionedApiClient(versionedFiles); final mainCode = _generateVersionedApiClient(versionedFiles);
final mainFilePath = '$apiDir/api_client.dart'; final mainFilePath = '$apiDir/api_client.dart';
// API // API
if (!ConfigLoader.shouldSkipFile(mainFilePath)) { if (!ConfigLoader.shouldSkipFile(mainFilePath)) {
await FileUtils.writeFile(mainFilePath, mainCode); await FileUtils.writeFile(mainFilePath, mainCode);
@ -280,20 +323,19 @@ class GenerateCommand extends BaseCommand {
final parameterEntityFiles = final parameterEntityFiles =
lastGenerator.generateParameterEntityFiles(); lastGenerator.generateParameterEntityFiles();
if (parameterEntityFiles.isNotEmpty) { if (parameterEntityFiles.isNotEmpty) {
final modelsDir = '$fullOutputDir/api_models';
// Parameters parameters // Parameters parameters
final parametersDir = '$modelsDir/parameters'; final parametersDir = '$modelsDir/parameters';
await FileUtils.ensureDirectoryExists(parametersDir); await FileUtils.ensureDirectoryExists(parametersDir);
for (final entry in parameterEntityFiles.entries) { for (final entry in parameterEntityFiles.entries) {
final filePath = '$parametersDir/${entry.key}'; final filePath = '$parametersDir/${entry.key}';
// //
if (ConfigLoader.shouldSkipFile(filePath)) { if (ConfigLoader.shouldSkipFile(filePath)) {
progress('跳过文件: $filePath'); progress('跳过文件: $filePath');
continue; continue;
} }
await FileUtils.writeFile(filePath, entry.value); await FileUtils.writeFile(filePath, entry.value);
success('参数实体类文件已保存到: $filePath'); success('参数实体类文件已保存到: $filePath');
generatedFiles++; generatedFiles++;
@ -307,7 +349,6 @@ class GenerateCommand extends BaseCommand {
// index.dart // index.dart
if (options.generateModels || options.generateApi) { if (options.generateModels || options.generateApi) {
progress('正在重新生成 index.dart 文件...'); progress('正在重新生成 index.dart 文件...');
final modelsDir = '$fullOutputDir/api_models';
final allFiles = await _getAllModelFiles(modelsDir); final allFiles = await _getAllModelFiles(modelsDir);
final indexContent = _generateUpdatedIndexFile(allFiles); final indexContent = _generateUpdatedIndexFile(allFiles);
final indexPath = '$modelsDir/index.dart'; final indexPath = '$modelsDir/index.dart';
@ -321,8 +362,8 @@ class GenerateCommand extends BaseCommand {
final generator = DocumentationGenerator(document); final generator = DocumentationGenerator(document);
final code = generator.generate(); final code = generator.generate();
final filePath = '$fullOutputDir/api_documentation.md'; final filePath = '$baseDir/api_documentation.md';
// //
if (!ConfigLoader.shouldSkipFile(filePath)) { if (!ConfigLoader.shouldSkipFile(filePath)) {
await FileUtils.writeFile(filePath, code); await FileUtils.writeFile(filePath, code);
@ -334,7 +375,7 @@ class GenerateCommand extends BaseCommand {
} }
// //
_generateSummary(document, fullOutputDir); _generateSummary(document, baseDir);
success('代码生成完成!共生成 $generatedFiles 个文件'); success('代码生成完成!共生成 $generatedFiles 个文件');
return 0; return 0;
@ -428,7 +469,13 @@ class GenerateCommand extends BaseCommand {
final fileName = path.basename(entity.path); final fileName = path.basename(entity.path);
// index.dart .g.dart // index.dart .g.dart
if (fileName != 'index.dart' && !fileName.endsWith('.g.dart')) { if (fileName != 'index.dart' && !fileName.endsWith('.g.dart')) {
dartFiles.add(fileName); // ignored_files
final filePath = path.join(subDir, fileName);
if (!ConfigLoader.shouldSkipFile(filePath)) {
dartFiles.add(fileName);
} else {
progress(' 跳过导出文件: $fileName (在 ignored_files 配置中)');
}
} }
} }
} }
@ -473,6 +520,22 @@ class GenerateCommand extends BaseCommand {
buffer.writeln('library;'); buffer.writeln('library;');
buffer.writeln(''); buffer.writeln('');
// base_result base_page_result
final baseResultImport = SwaggerConfig.baseResultImport;
final basePageResultImport = SwaggerConfig.basePageResultImport;
if (baseResultImport.isNotEmpty) {
buffer.writeln('export \'$baseResultImport\';');
}
if (basePageResultImport.isNotEmpty) {
buffer.writeln('export \'$basePageResultImport\';');
}
if ((baseResultImport.isNotEmpty || basePageResultImport.isNotEmpty) &&
fileNames.isNotEmpty) {
buffer.writeln('');
}
// //
for (final fileName in fileNames) { for (final fileName in fileNames) {
buffer.writeln('export \'$fileName\';'); buffer.writeln('export \'$fileName\';');
@ -513,7 +576,7 @@ class GenerateCommand extends BaseCommand {
// //
final pattern = ConfigLoader.getVersionExtractionPattern(); final pattern = ConfigLoader.getVersionExtractionPattern();
final defaultVersion = ConfigLoader.getDefaultVersion(); final defaultVersion = ConfigLoader.getDefaultVersion();
try { try {
final versionMatch = RegExp(pattern).firstMatch(path); final versionMatch = RegExp(pattern).firstMatch(path);
if (versionMatch != null && versionMatch.groupCount > 0) { if (versionMatch != null && versionMatch.groupCount > 0) {
@ -527,7 +590,7 @@ class GenerateCommand extends BaseCommand {
return 'v${versionMatch.group(1)}'; return 'v${versionMatch.group(1)}';
} }
} }
return defaultVersion; return defaultVersion;
} }

View File

@ -40,6 +40,12 @@ class SwaggerConfig {
/// ///
static String get modelsDir => ConfigLoader.getModelsDir(); static String get modelsDir => ConfigLoader.getModelsDir();
/// BaseResult
static String get baseResultImport => ConfigLoader.getBaseResultImport();
/// BasePageResult
static String get basePageResultImport => ConfigLoader.getBasePageResultImport();
/// ///
static const String defaultDocumentationFile = static const String defaultDocumentationFile =
'generated_api_documentation.md'; 'generated_api_documentation.md';

View File

@ -106,6 +106,10 @@ class ConfigLoader {
/// swagger_urls () /// swagger_urls ()
/// : ["url1", "url2"] /// : ["url1", "url2"]
/// : [{url: "...", enabled: true}] /// : [{url: "...", enabled: true}]
///
/// URL
/// Swagger
/// V2 V1
static List<String> getSwaggerUrls([Map<String, dynamic>? config]) { static List<String> getSwaggerUrls([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig(); final cfg = config ?? loadConfig();
if (cfg == null) { if (cfg == null) {
@ -430,4 +434,46 @@ class ConfigLoader {
return versionExtraction['default_version'] as String? ?? 'v1'; return versionExtraction['default_version'] as String? ?? 'v1';
} }
/// BaseResult
static String getBaseResultImport([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return 'package:learning_officer_oa/common/models/common/base_result.dart';
}
final generation = cfg['generation'] as Map<String, dynamic>?;
if (generation == null) {
return 'package:learning_officer_oa/common/models/common/base_result.dart';
}
final api = generation['api'] as Map<String, dynamic>?;
if (api == null) {
return 'package:learning_officer_oa/common/models/common/base_result.dart';
}
return api['base_result_import'] as String? ??
'package:learning_officer_oa/common/models/common/base_result.dart';
}
/// BasePageResult
static String getBasePageResultImport([Map<String, dynamic>? config]) {
final cfg = config ?? loadConfig();
if (cfg == null) {
return 'package:learning_officer_oa/common/models/common/base_page_result.dart';
}
final generation = cfg['generation'] as Map<String, dynamic>?;
if (generation == null) {
return 'package:learning_officer_oa/common/models/common/base_page_result.dart';
}
final api = generation['api'] as Map<String, dynamic>?;
if (api == null) {
return 'package:learning_officer_oa/common/models/common/base_page_result.dart';
}
return api['base_page_result_import'] as String? ??
'package:learning_officer_oa/common/models/common/base_page_result.dart';
}
} }

View File

@ -1,4 +1,5 @@
import '../core/models.dart'; import '../core/models.dart';
import '../core/config.dart';
import '../utils/string_utils.dart'; import '../utils/string_utils.dart';
import 'base_generator.dart'; import 'base_generator.dart';
@ -430,6 +431,22 @@ class ModelCodeGenerator extends ModelGenerator {
buffer.writeln('library;'); buffer.writeln('library;');
buffer.writeln(''); buffer.writeln('');
// base_result base_page_result
final baseResultImport = SwaggerConfig.baseResultImport;
final basePageResultImport = SwaggerConfig.basePageResultImport;
if (baseResultImport.isNotEmpty) {
buffer.writeln('export \'$baseResultImport\';');
}
if (basePageResultImport.isNotEmpty) {
buffer.writeln('export \'$basePageResultImport\';');
}
if ((baseResultImport.isNotEmpty || basePageResultImport.isNotEmpty) &&
modelsByDirectory.isNotEmpty) {
buffer.writeln('');
}
// index.dart // index.dart
final sortedDirs = modelsByDirectory.keys.toList()..sort(); final sortedDirs = modelsByDirectory.keys.toList()..sort();
@ -473,6 +490,22 @@ class ModelCodeGenerator extends ModelGenerator {
buffer.writeln('library;'); buffer.writeln('library;');
buffer.writeln(''); buffer.writeln('');
// base_result base_page_result
final baseResultImport = SwaggerConfig.baseResultImport;
final basePageResultImport = SwaggerConfig.basePageResultImport;
if (baseResultImport.isNotEmpty) {
buffer.writeln('export \'$baseResultImport\';');
}
if (basePageResultImport.isNotEmpty) {
buffer.writeln('export \'$basePageResultImport\';');
}
if ((baseResultImport.isNotEmpty || basePageResultImport.isNotEmpty) &&
modelFileNames.isNotEmpty) {
buffer.writeln('');
}
// //
final sortedFiles = List<String>.from(modelFileNames)..sort(); final sortedFiles = List<String>.from(modelFileNames)..sort();

View File

@ -135,17 +135,18 @@ class RetrofitApiGenerator extends BaseGenerator {
final buffer = StringBuffer(); final buffer = StringBuffer();
final fileName = _generateTagFileName(tagName); final fileName = _generateTagFileName(tagName);
// //
buffer.writeln(generateFileHeader('$tagName API 接口定义', fileName: fileName)); buffer
.writeln(generateFileHeader('$tagName API 接口定义', fileName: fileName));
buffer.writeln(''); buffer.writeln('');
// //
_generateTagImports(buffer, paths); _generateTagImports(buffer, paths);
// API // API
_generateTagApiInterface(buffer, tagName, paths); _generateTagApiInterface(buffer, tagName, paths);
apiFiles[fileName] = generateTypeCheckedCode(buffer.toString()); apiFiles[fileName] = generateTypeCheckedCode(buffer.toString());
} }
@ -154,12 +155,20 @@ class RetrofitApiGenerator extends BaseGenerator {
/// ///
void _generateImports(StringBuffer buffer) { void _generateImports(StringBuffer buffer) {
// // Dart
// 1. dart:xxx
// 2. package:xxx
// 3. package:project_name
// 4.
//
// dart:
buffer.writeln('import \'dart:convert\';'); buffer.writeln('import \'dart:convert\';');
buffer.writeln('import \'dart:io\';'); buffer.writeln('import \'dart:io\';');
buffer.writeln('import \'dart:typed_data\';'); buffer.writeln('import \'dart:typed_data\';');
buffer.writeln('');
// Dio Retrofit // package:
if (useDio) { if (useDio) {
buffer.writeln('import \'package:dio/dio.dart\';'); buffer.writeln('import \'package:dio/dio.dart\';');
} }
@ -173,19 +182,9 @@ class RetrofitApiGenerator extends BaseGenerator {
buffer.writeln('import \'package:crypto/crypto.dart\';'); buffer.writeln('import \'package:crypto/crypto.dart\';');
buffer.writeln('import \'package:path/path.dart\' as path;'); buffer.writeln('import \'package:path/path.dart\' as path;');
buffer.writeln('import \'package:http_parser/http_parser.dart\';'); buffer.writeln('import \'package:http_parser/http_parser.dart\';');
buffer.writeln(''); buffer.writeln('');
// // api_models/index.dart base_result base_page_result
buffer.writeln(
'import \'package:learning_officer_oa/common/models/common/base_result.dart\';');
buffer.writeln(
'import \'package:learning_officer_oa/common/models/common/base_page_result.dart\';');
buffer.writeln('');
// index.dart
// 使 index.dart
// Dart tree-shaking 使
buffer.writeln('import \'../../api_models/index.dart\';'); buffer.writeln('import \'../../api_models/index.dart\';');
buffer.writeln(''); buffer.writeln('');
@ -1238,16 +1237,8 @@ class RetrofitApiGenerator extends BaseGenerator {
buffer.writeln(''); buffer.writeln('');
// // api_models/index.dart base_result base_page_result
buffer.writeln( buffer.writeln('import \'../../api_models/index.dart\';');
'import \'package:learning_officer_oa/common/models/common/base_result.dart\';');
// base_page_result.dart
if (_needsPaginationImportForDocument()) {
buffer.writeln(
'import \'package:learning_officer_oa/common/models/common/base_page_result.dart\';');
}
buffer.writeln(''); buffer.writeln('');
// //
@ -1369,31 +1360,24 @@ class RetrofitApiGenerator extends BaseGenerator {
/// tag /// tag
void _generateTagImports(StringBuffer buffer, List<ApiPath> paths) { void _generateTagImports(StringBuffer buffer, List<ApiPath> paths) {
// Dart
// 1. dart:xxx
// 2. package:xxx
// 3. package:project_name
// 4.
//
// package:
if (useDio) { if (useDio) {
buffer.writeln('import \'package:dio/dio.dart\';'); buffer.writeln('import \'package:dio/dio.dart\';');
} }
if (useRetrofit) { if (useRetrofit) {
buffer.writeln('import \'package:retrofit/retrofit.dart\';'); buffer.writeln('import \'package:retrofit/retrofit.dart\';');
} }
buffer.writeln(''); buffer.writeln('');
// // api_models/index.dart base_result base_page_result
buffer.writeln(
'import \'package:learning_officer_oa/common/models/common/base_result.dart\';');
// base_page_result.dart
if (_needsPaginationImport(paths)) {
buffer.writeln(
'import \'package:learning_officer_oa/common/models/common/base_page_result.dart\';');
}
buffer.writeln('');
// index.dart
// 使 index.dart
// Dart tree-shaking 使
buffer.writeln('import \'../../api_models/index.dart\';'); buffer.writeln('import \'../../api_models/index.dart\';');
buffer.writeln(''); buffer.writeln('');
@ -1663,84 +1647,85 @@ class RetrofitApiGenerator extends BaseGenerator {
/// ///
void _generateParameterEntity( void _generateParameterEntity(
ApiPath path, String className, List<ApiParameter> queryParams) { ApiPath path, String className, List<ApiParameter> queryParams) {
if (!_generatedParameterEntities.containsKey(className)) { //
final buffer = StringBuffer(); //
// V2 V1
final buffer = StringBuffer();
// //
buffer.writeln(generateFileHeader( buffer.writeln(generateFileHeader(
'参数实体类 - $className', '参数实体类 - $className',
fileName: '${StringUtils.toSnakeCase(className)}.dart', fileName: '${StringUtils.toSnakeCase(className)}.dart',
)); ));
buffer.writeln('// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数'); buffer
buffer.writeln(''); .writeln('// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数');
buffer.writeln('');
// //
buffer buffer.writeln('import \'package:json_annotation/json_annotation.dart\';');
.writeln('import \'package:json_annotation/json_annotation.dart\';'); buffer.writeln('');
buffer.writeln(''); buffer.writeln('part \'${StringUtils.toSnakeCase(className)}.g.dart\';');
buffer.writeln('part \'${StringUtils.toSnakeCase(className)}.g.dart\';'); buffer.writeln('');
buffer.writeln('');
// //
buffer.writeln('@JsonSerializable(checked: true, includeIfNull: false)'); buffer.writeln('@JsonSerializable(checked: true, includeIfNull: false)');
buffer.writeln('class $className {'); buffer.writeln('class $className {');
// //
for (final param in queryParams) { for (final param in queryParams) {
final dartName = StringUtils.toDartPropertyName(param.name); final dartName = StringUtils.toDartPropertyName(param.name);
final dartType = _getDartType(param.type); final dartType = _getDartType(param.type);
final nullable = param.required ? '' : '?'; final nullable = param.required ? '' : '?';
// //
final cleanDescription = param.description final cleanDescription = param.description
.replaceAll('\r\n', ' ') .replaceAll('\r\n', ' ')
.replaceAll('\n', ' ') .replaceAll('\n', ' ')
.replaceAll('\r', ' ') .replaceAll('\r', ' ')
.trim(); .trim();
buffer.writeln(
' /// ${cleanDescription.isNotEmpty ? cleanDescription : param.name}');
buffer.writeln(' @JsonKey(name: \'${param.name}\')');
buffer.writeln(' final $dartType$nullable $dartName;');
buffer.writeln('');
}
//
buffer.writeln(' const $className({');
for (final param in queryParams) {
final dartName = StringUtils.toDartPropertyName(param.name);
final required = param.required ? 'required ' : '';
buffer.writeln(' ${required}this.$dartName,');
}
buffer.writeln(' });');
buffer.writeln('');
// fromJson
buffer.writeln( buffer.writeln(
' factory $className.fromJson(Map<String, dynamic> json) =>'); ' /// ${cleanDescription.isNotEmpty ? cleanDescription : param.name}');
buffer.writeln(' _\$${className}FromJson(json);'); buffer.writeln(' @JsonKey(name: \'${param.name}\')');
buffer.writeln(' final $dartType$nullable $dartName;');
buffer.writeln(''); buffer.writeln('');
// toJson
buffer.writeln(
' Map<String, dynamic> toJson() => _\$${className}ToJson(this);');
buffer.writeln('');
// toQueryMap Dio
buffer.writeln(' /// 转换为查询参数 Map');
buffer.writeln(' Map<String, dynamic> toQueryMap() {');
buffer.writeln(' final map = <String, dynamic>{};');
for (final param in queryParams) {
final dartName = StringUtils.toDartPropertyName(param.name);
buffer.writeln(
' if ($dartName != null) map[\'${param.name}\'] = $dartName;');
}
buffer.writeln(' return map;');
buffer.writeln(' }');
buffer.writeln('}');
_generatedParameterEntities[className] = buffer.toString();
} }
//
buffer.writeln(' const $className({');
for (final param in queryParams) {
final dartName = StringUtils.toDartPropertyName(param.name);
final required = param.required ? 'required ' : '';
buffer.writeln(' ${required}this.$dartName,');
}
buffer.writeln(' });');
buffer.writeln('');
// fromJson
buffer
.writeln(' factory $className.fromJson(Map<String, dynamic> json) =>');
buffer.writeln(' _\$${className}FromJson(json);');
buffer.writeln('');
// toJson
buffer.writeln(
' Map<String, dynamic> toJson() => _\$${className}ToJson(this);');
buffer.writeln('');
// toQueryMap Dio
buffer.writeln(' /// 转换为查询参数 Map');
buffer.writeln(' Map<String, dynamic> toQueryMap() {');
buffer.writeln(' final map = <String, dynamic>{};');
for (final param in queryParams) {
final dartName = StringUtils.toDartPropertyName(param.name);
buffer.writeln(
' if ($dartName != null) map[\'${param.name}\'] = $dartName;');
}
buffer.writeln(' return map;');
buffer.writeln(' }');
buffer.writeln('}');
_generatedParameterEntities[className] = buffer.toString();
} }
/// ///
@ -1767,7 +1752,12 @@ class RetrofitApiGenerator extends BaseGenerator {
/// generate /// generate
void ensureParameterEntitiesGenerated() { void ensureParameterEntitiesGenerated() {
// //
for (final path in document.paths.values) { // V2 V1
//
final sortedPaths = document.paths.values.toList()
..sort((a, b) => a.path.compareTo(b.path));
for (final path in sortedPaths) {
final queryParams = path.parameters final queryParams = path.parameters
.where((p) => p.location == ParameterLocation.query) .where((p) => p.location == ParameterLocation.query)
.toList(); .toList();

View File

@ -1,13 +1,55 @@
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
/// ///
/// ///
class FileUtils { class FileUtils {
///
///
static String resolvePath(String filePath) {
//
if (path.isAbsolute(filePath)) {
return filePath;
}
//
//
final configFile = _findConfigFile();
if (configFile != null) {
final configDir = path.dirname(configFile);
return path.join(configDir, filePath);
}
// 使
return path.join(Directory.current.path, filePath);
}
///
static String? _findConfigFile() {
var currentDir = Directory.current;
final maxDepth = 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 Future<Directory> ensureDirectoryExists(String dirPath) async { static Future<Directory> ensureDirectoryExists(String dirPath) async {
final directory = Directory(dirPath); final resolvedPath = resolvePath(dirPath);
final directory = Directory(resolvedPath);
if (!await directory.exists()) { if (!await directory.exists()) {
await directory.create(recursive: true); await directory.create(recursive: true);
} }
@ -17,7 +59,8 @@ class FileUtils {
/// ///
static Future<void> safeWriteFile(String filePath, String content) async { static Future<void> safeWriteFile(String filePath, String content) async {
try { try {
final file = File(filePath); final resolvedPath = resolvePath(filePath);
final file = File(resolvedPath);
final directory = file.parent; final directory = file.parent;
// //

View File

@ -386,7 +386,6 @@ class StringUtils {
final copyright = ConfigLoader.getCopyright(); final copyright = ConfigLoader.getCopyright();
return '''// $description return '''// $description
// Swagger API : $source
// $generatorName by $author // $generatorName by $author
// $copyright // $copyright
'''; ''';

View File

@ -30,11 +30,11 @@ dependencies:
dio: ^5.0.0 dio: ^5.0.0
retrofit: ^4.0.0 retrofit: ^4.0.0
json_annotation: ^4.8.1 json_annotation: ^4.8.1
dev_dependencies: dev_dependencies:
# 测试框架 # 测试框架
test: ^1.24.0 test: ^1.24.0
# 代码生成工具(仅用于测试/示例) # 代码生成工具(仅用于测试/示例)
build_runner: ^2.4.7 build_runner: ^2.4.7
json_serializable: ^6.7.1 json_serializable: ^6.7.1