feat: base page support

This commit is contained in:
Max 2025-07-14 18:46:43 +08:00
parent bb7529273b
commit d9e46be9dd
2 changed files with 227 additions and 11 deletions

View File

@ -287,8 +287,13 @@ class ModelCodeGenerator extends ModelGenerator {
Map<String, String> generateSeparateModelFiles() {
final files = <String, String>{};
//
//
for (final model in document.models.values) {
// total items
if (_isPaginationResponseModel(model)) {
continue; // 使 BasePageResult<T>
}
final fileName = StringUtils.generateFileName(model.name);
final content = generateSingleModelFile(model);
files[fileName] = content;
@ -296,6 +301,7 @@ class ModelCodeGenerator extends ModelGenerator {
// index.dart
final modelFileNames = document.models.keys
.where((name) => !_isPaginationResponseModel(document.models[name]!))
.map((name) => StringUtils.generateFileName(name))
.toList();
final indexContent = generateIndexFile(modelFileNames);
@ -679,4 +685,25 @@ class ModelCodeGenerator extends ModelGenerator {
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;
}
}

View File

@ -118,7 +118,7 @@ class RetrofitApiGenerator extends BaseGenerator {
final modelImports = _getRequiredModelImports();
for (final modelImport in modelImports) {
buffer.writeln(
'import \'../api_models/${StringUtils.generateFileName(modelImport)}\';');
'import \'../../api_models/${StringUtils.generateFileName(modelImport)}\';');
}
if (modelImports.isNotEmpty) {
@ -281,18 +281,115 @@ class RetrofitApiGenerator extends BaseGenerator {
/// BaseResult或BasePageResult
String _wrapWithBaseResult(String originalType, ApiPath? path) {
//
if (originalType.startsWith('List<') &&
_isPageableType(originalType, path)) {
// List中的类型
// void
if (originalType == 'void') {
return 'BaseResult<void>';
}
//
if (originalType.startsWith('List<')) {
// schema total items使 BasePageResult
if (_isPaginationResponseFromSchema(originalType, path)) {
final innerType = originalType.substring(5, originalType.length - 1);
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包装
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) {
if (path == null) {
@ -326,8 +423,8 @@ class RetrofitApiGenerator extends BaseGenerator {
score += 0.5;
}
//
return score >= 4;
//
return score >= 2;
}
///
@ -466,6 +563,40 @@ class RetrofitApiGenerator extends BaseGenerator {
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) {
// content.application/json.schema (Swagger 3.0)
@ -502,6 +633,27 @@ class RetrofitApiGenerator extends BaseGenerator {
final refName = parts.last;
//
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);
}
//
@ -522,6 +674,34 @@ class RetrofitApiGenerator extends BaseGenerator {
if (schema['type'] == 'object') {
// properties
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>
return 'Map<String, dynamic>';
}
@ -1134,7 +1314,7 @@ class RetrofitApiGenerator extends BaseGenerator {
final modelImports = _getRequiredModelImportsForPaths(paths);
for (final modelImport in modelImports) {
buffer.writeln(
'import \'../api_models/${StringUtils.generateFileName(modelImport)}\';');
'import \'../../api_models/${StringUtils.generateFileName(modelImport)}\';');
}
if (modelImports.isNotEmpty) {
@ -1259,6 +1439,11 @@ class RetrofitApiGenerator extends BaseGenerator {
return;
}
// PageResponse
if (cleanType.endsWith('PageResponse')) {
return; // 使 BasePageResult<T>
}
// 使
imports.add(StringUtils.generateClassName(cleanType));
}
@ -1482,6 +1667,10 @@ class RetrofitApiGenerator extends BaseGenerator {
String? _inferTypeFromPathKeywords(
String pathLower, String summaryLower, List<String> tags) {
//
if (pathLower.contains('healthcheck') || pathLower.contains('health')) {
return 'void';
}
if (pathLower.contains('/login/')) {
return 'UserLoginResult';
}