Compare commits
3 Commits
b47a823db4
...
c1a6c94357
| Author | SHA1 | Date |
|---|---|---|
|
|
c1a6c94357 | |
|
|
ca57ceb354 | |
|
|
ff9bb34a31 |
|
|
@ -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
|
|
@ -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,7 +180,6 @@ 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();
|
||||||
|
|
@ -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
|
||||||
|
|
@ -280,7 +323,6 @@ 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);
|
||||||
|
|
@ -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,7 +362,7 @@ 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)) {
|
||||||
|
|
@ -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')) {
|
||||||
|
// 检查文件是否在 ignored_files 配置中
|
||||||
|
final filePath = path.join(subDir, fileName);
|
||||||
|
if (!ConfigLoader.shouldSkipFile(filePath)) {
|
||||||
dartFiles.add(fileName);
|
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\';');
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,8 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
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('');
|
||||||
|
|
||||||
// 生成导入语句
|
// 生成导入语句
|
||||||
|
|
@ -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,7 +1647,9 @@ 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)) {
|
// 注意:如果类名已存在,会覆盖之前的定义
|
||||||
|
// 这样可以确保后面版本的路径覆盖前面版本的参数实体类定义
|
||||||
|
// 例如:V2 的参数实体类会覆盖 V1 的同名参数实体类
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
// 生成文件头注释
|
// 生成文件头注释
|
||||||
|
|
@ -1671,12 +1657,12 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
'参数实体类 - $className',
|
'参数实体类 - $className',
|
||||||
fileName: '${StringUtils.toSnakeCase(className)}.dart',
|
fileName: '${StringUtils.toSnakeCase(className)}.dart',
|
||||||
));
|
));
|
||||||
buffer.writeln('// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数');
|
buffer
|
||||||
|
.writeln('// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数');
|
||||||
buffer.writeln('');
|
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('');
|
||||||
|
|
@ -1715,8 +1701,8 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
buffer.writeln('');
|
buffer.writeln('');
|
||||||
|
|
||||||
// 生成 fromJson 方法
|
// 生成 fromJson 方法
|
||||||
buffer.writeln(
|
buffer
|
||||||
' factory $className.fromJson(Map<String, dynamic> json) =>');
|
.writeln(' factory $className.fromJson(Map<String, dynamic> json) =>');
|
||||||
buffer.writeln(' _\$${className}FromJson(json);');
|
buffer.writeln(' _\$${className}FromJson(json);');
|
||||||
buffer.writeln('');
|
buffer.writeln('');
|
||||||
|
|
||||||
|
|
@ -1741,7 +1727,6 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
|
|
||||||
_generatedParameterEntities[className] = buffer.toString();
|
_generatedParameterEntities[className] = buffer.toString();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// 存储已生成的参数实体类
|
/// 存储已生成的参数实体类
|
||||||
final Map<String, String> _generatedParameterEntities = {};
|
final Map<String, String> _generatedParameterEntities = {};
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
''';
|
''';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue