feat: base page support
This commit is contained in:
parent
bb7529273b
commit
d9e46be9dd
|
|
@ -287,8 +287,13 @@ class ModelCodeGenerator extends ModelGenerator {
|
||||||
Map<String, String> generateSeparateModelFiles() {
|
Map<String, String> generateSeparateModelFiles() {
|
||||||
final files = <String, String>{};
|
final files = <String, String>{};
|
||||||
|
|
||||||
// 生成所有模型文件
|
// 生成所有模型文件,但过滤掉分页响应文件
|
||||||
for (final model in document.models.values) {
|
for (final model in document.models.values) {
|
||||||
|
// 检查是否是分页响应模型(包含 total 和 items 字段)
|
||||||
|
if (_isPaginationResponseModel(model)) {
|
||||||
|
continue; // 跳过分页响应模型,使用统一的 BasePageResult<T>
|
||||||
|
}
|
||||||
|
|
||||||
final fileName = StringUtils.generateFileName(model.name);
|
final fileName = StringUtils.generateFileName(model.name);
|
||||||
final content = generateSingleModelFile(model);
|
final content = generateSingleModelFile(model);
|
||||||
files[fileName] = content;
|
files[fileName] = content;
|
||||||
|
|
@ -296,6 +301,7 @@ class ModelCodeGenerator extends ModelGenerator {
|
||||||
|
|
||||||
// 生成 index.dart 文件
|
// 生成 index.dart 文件
|
||||||
final modelFileNames = document.models.keys
|
final modelFileNames = document.models.keys
|
||||||
|
.where((name) => !_isPaginationResponseModel(document.models[name]!))
|
||||||
.map((name) => StringUtils.generateFileName(name))
|
.map((name) => StringUtils.generateFileName(name))
|
||||||
.toList();
|
.toList();
|
||||||
final indexContent = generateIndexFile(modelFileNames);
|
final indexContent = generateIndexFile(modelFileNames);
|
||||||
|
|
@ -679,4 +685,25 @@ class ModelCodeGenerator extends ModelGenerator {
|
||||||
|
|
||||||
return annotations.join(', ');
|
return annotations.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查是否是分页响应模型(包含 total 和 items 字段)
|
||||||
|
bool _isPaginationResponseModel(ApiModel model) {
|
||||||
|
// 检查是否包含 total 和 items 字段
|
||||||
|
if (!model.properties.containsKey('total') ||
|
||||||
|
!model.properties.containsKey('items')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalProp = model.properties['total']!;
|
||||||
|
final itemsProp = model.properties['items']!;
|
||||||
|
|
||||||
|
// 检查 total 字段是否为数字类型
|
||||||
|
final isTotalNumeric = totalProp.type == PropertyType.integer ||
|
||||||
|
totalProp.type == PropertyType.number;
|
||||||
|
|
||||||
|
// 检查 items 字段是否为数组类型
|
||||||
|
final isItemsArray = itemsProp.type == PropertyType.array;
|
||||||
|
|
||||||
|
return isTotalNumeric && isItemsArray;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
final modelImports = _getRequiredModelImports();
|
final modelImports = _getRequiredModelImports();
|
||||||
for (final modelImport in modelImports) {
|
for (final modelImport in modelImports) {
|
||||||
buffer.writeln(
|
buffer.writeln(
|
||||||
'import \'../api_models/${StringUtils.generateFileName(modelImport)}\';');
|
'import \'../../api_models/${StringUtils.generateFileName(modelImport)}\';');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelImports.isNotEmpty) {
|
if (modelImports.isNotEmpty) {
|
||||||
|
|
@ -281,18 +281,115 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
|
|
||||||
/// 包装返回类型为BaseResult或BasePageResult
|
/// 包装返回类型为BaseResult或BasePageResult
|
||||||
String _wrapWithBaseResult(String originalType, ApiPath? path) {
|
String _wrapWithBaseResult(String originalType, ApiPath? path) {
|
||||||
// 检查是否是列表类型且可能需要分页
|
// 特殊处理 void 类型(如健康检查接口)
|
||||||
if (originalType.startsWith('List<') &&
|
if (originalType == 'void') {
|
||||||
_isPageableType(originalType, path)) {
|
return 'BaseResult<void>';
|
||||||
// 提取List中的类型
|
}
|
||||||
|
|
||||||
|
// 检查是否是列表类型
|
||||||
|
if (originalType.startsWith('List<')) {
|
||||||
|
// 如果是从 schema 中识别出的分页响应(包含 total 和 items),使用 BasePageResult
|
||||||
|
if (_isPaginationResponseFromSchema(originalType, path)) {
|
||||||
final innerType = originalType.substring(5, originalType.length - 1);
|
final innerType = originalType.substring(5, originalType.length - 1);
|
||||||
return 'BaseResult<BasePageResult<$innerType>>';
|
return 'BaseResult<BasePageResult<$innerType>>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果响应 schema 直接返回数组(没有 total 和 items),使用 List
|
||||||
|
if (path != null && _isDirectArrayResponse(path)) {
|
||||||
|
return 'BaseResult<$originalType>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他列表类型,检查是否需要分页
|
||||||
|
if (_isPageableType(originalType, path)) {
|
||||||
|
final innerType = originalType.substring(5, originalType.length - 1);
|
||||||
|
return 'BaseResult<BasePageResult<$innerType>>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 对于其他类型,使用BaseResult包装
|
// 对于其他类型,使用BaseResult包装
|
||||||
return 'BaseResult<$originalType>';
|
return 'BaseResult<$originalType>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查是否是从 schema 中识别出的分页响应
|
||||||
|
bool _isPaginationResponseFromSchema(String type, ApiPath? path) {
|
||||||
|
if (path == null) return false;
|
||||||
|
|
||||||
|
// 检查响应 schema 是否包含 total 和 items 字段
|
||||||
|
final successResponses = ['200', '201', '202'];
|
||||||
|
|
||||||
|
for (final statusCode in successResponses) {
|
||||||
|
final response = path.responses[statusCode];
|
||||||
|
if (response != null) {
|
||||||
|
// 检查 content.application/json.schema (Swagger 3.0)
|
||||||
|
if (response.content != null) {
|
||||||
|
final applicationJson =
|
||||||
|
response.content!['application/json'] as Map<String, dynamic>?;
|
||||||
|
if (applicationJson != null) {
|
||||||
|
final schema = applicationJson['schema'] as Map<String, dynamic>?;
|
||||||
|
if (schema != null && _hasPaginationSchema(schema)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 schema 字段 (Swagger 2.0)
|
||||||
|
if (response.schema != null && _hasPaginationSchema(response.schema!)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查是否是分页响应模型(包含 total 和 items 字段)
|
||||||
|
bool _isPaginationResponseModel(ApiModel model) {
|
||||||
|
// 检查是否包含 total 和 items 字段
|
||||||
|
if (!model.properties.containsKey('total') ||
|
||||||
|
!model.properties.containsKey('items')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalProp = model.properties['total']!;
|
||||||
|
final itemsProp = model.properties['items']!;
|
||||||
|
|
||||||
|
// 检查 total 字段是否为数字类型
|
||||||
|
final isTotalNumeric = totalProp.type == PropertyType.integer ||
|
||||||
|
totalProp.type == PropertyType.number;
|
||||||
|
|
||||||
|
// 检查 items 字段是否为数组类型
|
||||||
|
final isItemsArray = itemsProp.type == PropertyType.array;
|
||||||
|
|
||||||
|
return isTotalNumeric && isItemsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查 schema 是否包含分页结构(total 和 items 字段)
|
||||||
|
bool _hasPaginationSchema(Map<String, dynamic> schema) {
|
||||||
|
if (schema['type'] != 'object') return false;
|
||||||
|
|
||||||
|
final properties = schema['properties'] as Map<String, dynamic>?;
|
||||||
|
if (properties == null) return false;
|
||||||
|
|
||||||
|
// 检查是否包含 total 和 items 字段
|
||||||
|
if (!properties.containsKey('total') || !properties.containsKey('items')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalProp = properties['total'] as Map<String, dynamic>?;
|
||||||
|
final itemsProp = properties['items'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
// 检查 total 字段是否为数字类型
|
||||||
|
final isTotalNumeric = totalProp != null &&
|
||||||
|
(totalProp['type'] == 'integer' || totalProp['type'] == 'number');
|
||||||
|
|
||||||
|
// 检查 items 字段是否为数组类型
|
||||||
|
final isItemsArray = itemsProp != null &&
|
||||||
|
itemsProp['type'] == 'array' &&
|
||||||
|
itemsProp['items'] != null;
|
||||||
|
|
||||||
|
return isTotalNumeric && isItemsArray;
|
||||||
|
}
|
||||||
|
|
||||||
/// 智能判断是否是可分页的类型
|
/// 智能判断是否是可分页的类型
|
||||||
bool _isPageableType(String type, ApiPath? path) {
|
bool _isPageableType(String type, ApiPath? path) {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
|
|
@ -326,8 +423,8 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
score += 0.5;
|
score += 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 需要达到阈值才认为是分页
|
// 降低阈值,让更多列表类型被识别为分页
|
||||||
return score >= 4;
|
return score >= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查是否包含分页相关的关键词
|
/// 检查是否包含分页相关的关键词
|
||||||
|
|
@ -466,6 +563,40 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
return paginationPathPatterns.any((pattern) => pattern.hasMatch(pathLower));
|
return paginationPathPatterns.any((pattern) => pattern.hasMatch(pathLower));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查响应是否直接返回数组(没有 total 和 items 字段)
|
||||||
|
bool _isDirectArrayResponse(ApiPath path) {
|
||||||
|
final successResponses = ['200', '201', '202'];
|
||||||
|
|
||||||
|
for (final statusCode in successResponses) {
|
||||||
|
final response = path.responses[statusCode];
|
||||||
|
if (response != null) {
|
||||||
|
// 检查 content.application/json.schema (Swagger 3.0)
|
||||||
|
if (response.content != null) {
|
||||||
|
final applicationJson =
|
||||||
|
response.content!['application/json'] as Map<String, dynamic>?;
|
||||||
|
if (applicationJson != null) {
|
||||||
|
final schema = applicationJson['schema'] as Map<String, dynamic>?;
|
||||||
|
if (schema != null && _isArraySchema(schema)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 schema 字段 (Swagger 2.0)
|
||||||
|
if (response.schema != null && _isArraySchema(response.schema!)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查 schema 是否为数组类型
|
||||||
|
bool _isArraySchema(Map<String, dynamic> schema) {
|
||||||
|
return schema['type'] == 'array';
|
||||||
|
}
|
||||||
|
|
||||||
/// 从响应中提取返回类型
|
/// 从响应中提取返回类型
|
||||||
String? _extractResponseType(ApiResponse response) {
|
String? _extractResponseType(ApiResponse response) {
|
||||||
// 优先检查 content.application/json.schema (Swagger 3.0)
|
// 优先检查 content.application/json.schema (Swagger 3.0)
|
||||||
|
|
@ -502,6 +633,27 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
final refName = parts.last;
|
final refName = parts.last;
|
||||||
// 检查是否是已知的模型类型
|
// 检查是否是已知的模型类型
|
||||||
if (document.models.containsKey(refName)) {
|
if (document.models.containsKey(refName)) {
|
||||||
|
final model = document.models[refName]!;
|
||||||
|
// 检查是否是分页响应模型
|
||||||
|
if (_isPaginationResponseModel(model)) {
|
||||||
|
// 提取 items 中的类型
|
||||||
|
final itemsProp = model.properties['items'];
|
||||||
|
if (itemsProp != null && itemsProp.type == PropertyType.array) {
|
||||||
|
String itemType = 'dynamic';
|
||||||
|
if (itemsProp.reference != null) {
|
||||||
|
itemType = StringUtils.generateClassName(itemsProp.reference!);
|
||||||
|
} else if (itemsProp.items != null) {
|
||||||
|
// itemsProp.items 可能是 ApiModel,尝试用 name
|
||||||
|
itemType = StringUtils.generateClassName(itemsProp.items!.name);
|
||||||
|
} else if (itemsProp.name.isNotEmpty) {
|
||||||
|
itemType = StringUtils.generateClassName(itemsProp.name);
|
||||||
|
} else if (itemsProp.type != PropertyType.array &&
|
||||||
|
itemsProp.type != PropertyType.reference) {
|
||||||
|
itemType = itemsProp.type.value;
|
||||||
|
}
|
||||||
|
return 'List<$itemType>';
|
||||||
|
}
|
||||||
|
}
|
||||||
return StringUtils.generateClassName(refName);
|
return StringUtils.generateClassName(refName);
|
||||||
}
|
}
|
||||||
// 尝试生成类名
|
// 尝试生成类名
|
||||||
|
|
@ -522,6 +674,34 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
if (schema['type'] == 'object') {
|
if (schema['type'] == 'object') {
|
||||||
// 检查是否有 properties 定义
|
// 检查是否有 properties 定义
|
||||||
if (schema['properties'] != null) {
|
if (schema['properties'] != null) {
|
||||||
|
final properties = schema['properties'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// 检查是否是分页响应格式(包含 total 和 items 字段)
|
||||||
|
if (properties.containsKey('total') &&
|
||||||
|
properties.containsKey('items')) {
|
||||||
|
final totalProp = properties['total'] as Map<String, dynamic>?;
|
||||||
|
final itemsProp = properties['items'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
// 检查 total 字段是否为数字类型
|
||||||
|
final isTotalNumeric = totalProp != null &&
|
||||||
|
(totalProp['type'] == 'integer' || totalProp['type'] == 'number');
|
||||||
|
|
||||||
|
// 检查 items 字段是否为数组类型
|
||||||
|
final isItemsArray = itemsProp != null &&
|
||||||
|
itemsProp['type'] == 'array' &&
|
||||||
|
itemsProp['items'] != null;
|
||||||
|
|
||||||
|
if (isTotalNumeric && isItemsArray) {
|
||||||
|
// 这是一个分页响应,提取 items 中的类型
|
||||||
|
final itemsSchema = itemsProp['items'] as Map<String, dynamic>;
|
||||||
|
final itemType = _extractTypeFromSchema(itemsSchema);
|
||||||
|
if (itemType != null) {
|
||||||
|
// 返回 List<T> 格式,让 _wrapWithBaseResult 处理为 BasePageResult<T>
|
||||||
|
return 'List<$itemType>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 这是一个复杂对象,返回 Map<String, dynamic>
|
// 这是一个复杂对象,返回 Map<String, dynamic>
|
||||||
return 'Map<String, dynamic>';
|
return 'Map<String, dynamic>';
|
||||||
}
|
}
|
||||||
|
|
@ -1134,7 +1314,7 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
final modelImports = _getRequiredModelImportsForPaths(paths);
|
final modelImports = _getRequiredModelImportsForPaths(paths);
|
||||||
for (final modelImport in modelImports) {
|
for (final modelImport in modelImports) {
|
||||||
buffer.writeln(
|
buffer.writeln(
|
||||||
'import \'../api_models/${StringUtils.generateFileName(modelImport)}\';');
|
'import \'../../api_models/${StringUtils.generateFileName(modelImport)}\';');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelImports.isNotEmpty) {
|
if (modelImports.isNotEmpty) {
|
||||||
|
|
@ -1259,6 +1439,11 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否是分页响应类型(以 PageResponse 结尾)
|
||||||
|
if (cleanType.endsWith('PageResponse')) {
|
||||||
|
return; // 跳过分页响应类型,因为我们已经使用 BasePageResult<T> 替代
|
||||||
|
}
|
||||||
|
|
||||||
// 添加模型类型到导入列表(使用转换后的类名)
|
// 添加模型类型到导入列表(使用转换后的类名)
|
||||||
imports.add(StringUtils.generateClassName(cleanType));
|
imports.add(StringUtils.generateClassName(cleanType));
|
||||||
}
|
}
|
||||||
|
|
@ -1482,6 +1667,10 @@ class RetrofitApiGenerator extends BaseGenerator {
|
||||||
String? _inferTypeFromPathKeywords(
|
String? _inferTypeFromPathKeywords(
|
||||||
String pathLower, String summaryLower, List<String> tags) {
|
String pathLower, String summaryLower, List<String> tags) {
|
||||||
// 基于路径关键词的智能推断
|
// 基于路径关键词的智能推断
|
||||||
|
if (pathLower.contains('healthcheck') || pathLower.contains('health')) {
|
||||||
|
return 'void';
|
||||||
|
}
|
||||||
|
|
||||||
if (pathLower.contains('/login/')) {
|
if (pathLower.contains('/login/')) {
|
||||||
return 'UserLoginResult';
|
return 'UserLoginResult';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue