diff --git a/build.yaml b/build.yaml index 0842dfb..69c8dcd 100644 --- a/build.yaml +++ b/build.yaml @@ -2,7 +2,9 @@ targets: $default: sources: - "lib/**" + - "generator/**" builders: json_serializable: generate_for: - - "lib/**" \ No newline at end of file + - "lib/**" + - "generator/**" \ No newline at end of file diff --git a/lib/generators/endpoint_code_generator.dart b/lib/generators/endpoint_code_generator.dart index 44e6c0d..9b41b63 100644 --- a/lib/generators/endpoint_code_generator.dart +++ b/lib/generators/endpoint_code_generator.dart @@ -60,10 +60,10 @@ class EndpointCodeGenerator extends BaseGenerator { // 生成注释 if (path.summary.isNotEmpty) { - buffer.writeln(' /// ${path.summary}'); + buffer.writeln(' ${StringUtils.generateComment(path.summary)}'); } if (path.description.isNotEmpty && path.description != path.summary) { - buffer.writeln(' /// ${path.description}'); + buffer.writeln(' ${StringUtils.generateComment(path.description)}'); } buffer.writeln(' static const String $constantName = \'$cleanPath\';'); @@ -171,7 +171,7 @@ class EndpointCodeGenerator extends BaseGenerator { final constantName = _generateConstantName(path); if (path.summary.isNotEmpty) { - buffer.writeln(' /// ${path.summary}'); + buffer.writeln(' ${StringUtils.generateComment(path.summary)}'); } buffer.writeln( ' $enumName(ApiPaths.$constantName, \'${path.method.value}\'),'); diff --git a/lib/generators/retrofit_api_generator.dart b/lib/generators/retrofit_api_generator.dart index cd1d7d4..007f1fd 100644 --- a/lib/generators/retrofit_api_generator.dart +++ b/lib/generators/retrofit_api_generator.dart @@ -181,10 +181,10 @@ class RetrofitApiGenerator extends BaseGenerator { // 生成方法注释 if (path.summary.isNotEmpty) { - buffer.writeln(' /// ${path.summary}'); + buffer.writeln(' ${StringUtils.generateComment(path.summary)}'); } if (path.description.isNotEmpty && path.description != path.summary) { - buffer.writeln(' /// ${path.description}'); + buffer.writeln(' ${StringUtils.generateComment(path.description)}'); } // 生成 HTTP 方法注解 @@ -226,8 +226,8 @@ class RetrofitApiGenerator extends BaseGenerator { if (operationId.toLowerCase().startsWith(method)) { return StringUtils.toCamelCase(operationId); } - // 否则添加 HTTP 方法前缀 - return '$method${StringUtils.toPascalCase(operationId)}'; + // 否则直接使用 operationId,不添加 HTTP 方法前缀 + return StringUtils.toCamelCase(operationId); } // 清理路径,移除 /api/v1 前缀 @@ -247,34 +247,18 @@ class RetrofitApiGenerator extends BaseGenerator { // 如 /TaskSummarize/GetSummarizeTaskByDate -> getSummarizeTaskByDate final action = StringUtils.toPascalCase(pathParts[1]); - // 如果方法名已经以HTTP方法开头,移除重复前缀 - final actionLower = action.toLowerCase(); - if (actionLower.startsWith(method)) { - // 移除重复的HTTP方法前缀 - final cleanAction = action.substring(method.length); - return '$method${StringUtils.toPascalCase(cleanAction)}'; - } - - return '$method$action'; + return StringUtils.toCamelCase(action); } else if (pathParts.length == 1) { - // 只有一个部分:如 /HealthCheck -> getHealthCheck + // 只有一个部分:如 /HealthCheck -> healthCheck final action = StringUtils.toPascalCase(pathParts[0]); - // 如果方法名已经以HTTP方法开头,移除重复前缀 - final actionLower = action.toLowerCase(); - if (actionLower.startsWith(method)) { - // 移除重复的HTTP方法前缀 - final cleanAction = action.substring(method.length); - return '$method${StringUtils.toPascalCase(cleanAction)}'; - } - - return '$method$action'; + return StringUtils.toCamelCase(action); } // 最后的备用方案:使用完整路径 final sanitizedPath = pathParts.map((part) => StringUtils.toPascalCase(part)).join(''); - return '$method$sanitizedPath'; + return StringUtils.toCamelCase(sanitizedPath); } /// 生成返回类型 @@ -649,7 +633,7 @@ class RetrofitApiGenerator extends BaseGenerator { .toList(); for (final param in pathParams) { parameters.add(ApiMethodParameter( - name: StringUtils.toCamelCase(param.name), + name: StringUtils.toDartPropertyName(param.name), type: _getDartType(param.type), annotation: useRetrofit ? '@Path(\'${param.name}\')' : '', required: param.required, @@ -680,7 +664,7 @@ class RetrofitApiGenerator extends BaseGenerator { for (final param in queryParams) { final nullable = param.required ? '' : '?'; parameters.add(ApiMethodParameter( - name: StringUtils.toCamelCase(param.name), + name: StringUtils.toDartPropertyName(param.name), type: '${_getDartType(param.type)}$nullable', annotation: useRetrofit ? '@Query(\'${param.name}\')' : '', required: param.required, @@ -695,7 +679,7 @@ class RetrofitApiGenerator extends BaseGenerator { for (final param in bodyParams) { final bodyType = _inferRequestBodyType(path); parameters.add(ApiMethodParameter( - name: StringUtils.toCamelCase( + name: StringUtils.toDartPropertyName( param.name.isNotEmpty ? param.name : 'request'), type: bodyType, annotation: useRetrofit ? '@Body()' : '', @@ -1037,7 +1021,7 @@ class RetrofitApiGenerator extends BaseGenerator { for (final tagName in tagGroups.keys) { final className = _generateTagClassName(tagName); buffer.writeln( - ' late final $className _${StringUtils.toCamelCase(tagName)}Api;'); + ' late final $className _${StringUtils.toDartPropertyName(tagName)}Api;'); } buffer.writeln(''); @@ -1046,7 +1030,7 @@ class RetrofitApiGenerator extends BaseGenerator { buffer.writeln(' $className(this._dio, {String? baseUrl}) {'); for (final tagName in tagGroups.keys) { final className = _generateTagClassName(tagName); - final fieldName = '_${StringUtils.toCamelCase(tagName)}Api'; + final fieldName = '_${StringUtils.toDartPropertyName(tagName)}Api'; buffer.writeln(' $fieldName = $className(_dio, baseUrl: baseUrl);'); } buffer.writeln(' }'); @@ -1056,10 +1040,10 @@ class RetrofitApiGenerator extends BaseGenerator { // 为每个 tag 生成一个获取器 for (final tagName in tagGroups.keys) { final className = _generateTagClassName(tagName); - final fieldName = '_${StringUtils.toCamelCase(tagName)}Api'; + final fieldName = '_${StringUtils.toDartPropertyName(tagName)}Api'; buffer.writeln(' /// ${tagName}相关API'); buffer.writeln( - ' $className get ${StringUtils.toCamelCase(tagName)} => $fieldName;'); + ' $className get ${StringUtils.toDartPropertyName(tagName)} => $fieldName;'); buffer.writeln(''); } @@ -1343,9 +1327,9 @@ class RetrofitApiGenerator extends BaseGenerator { final buffer = StringBuffer(); // 生成文件头注释 - buffer.writeln('/// 参数实体类 - $className'); + buffer.writeln('// 参数实体类 - $className'); buffer.writeln( - '/// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数'); + '// 用于 ${path.method.value.toUpperCase()} ${path.path} 的查询参数'); buffer.writeln(''); // 导入语句 @@ -1361,7 +1345,7 @@ class RetrofitApiGenerator extends BaseGenerator { // 生成属性 for (final param in queryParams) { - final dartName = StringUtils.toCamelCase(param.name); + final dartName = StringUtils.toDartPropertyName(param.name); final dartType = _getDartType(param.type); final nullable = param.required ? '' : '?'; @@ -1381,7 +1365,7 @@ class RetrofitApiGenerator extends BaseGenerator { // 生成构造函数 buffer.writeln(' const $className({'); for (final param in queryParams) { - final dartName = StringUtils.toCamelCase(param.name); + final dartName = StringUtils.toDartPropertyName(param.name); final required = param.required ? 'required ' : ''; buffer.writeln(' ${required}this.$dartName,'); } @@ -1404,7 +1388,7 @@ class RetrofitApiGenerator extends BaseGenerator { buffer.writeln(' Map toQueryMap() {'); buffer.writeln(' final map = {};'); for (final param in queryParams) { - final dartName = StringUtils.toCamelCase(param.name); + final dartName = StringUtils.toDartPropertyName(param.name); buffer.writeln( ' if ($dartName != null) map[\'${param.name}\'] = $dartName;'); } diff --git a/lib/utils/string_utils.dart b/lib/utils/string_utils.dart index 45fc0c5..ec1acc5 100644 --- a/lib/utils/string_utils.dart +++ b/lib/utils/string_utils.dart @@ -26,6 +26,17 @@ class StringUtils { static String toCamelCase(String input) { if (input.isEmpty) return input; + // 如果输入已经是camelCase格式(首字母小写),直接返回 + if (RegExp(r'^[a-z][a-zA-Z0-9]*$').hasMatch(input)) { + return input; + } + + // 如果输入是PascalCase格式(首字母大写),转换为camelCase + if (RegExp(r'^[A-Z][a-zA-Z0-9]*$').hasMatch(input)) { + return input[0].toLowerCase() + input.substring(1); + } + + // 处理下划线分隔的字符串 final parts = input.split('_').where((p) => p.isNotEmpty).toList(); if (parts.isEmpty) return input; @@ -95,6 +106,7 @@ class StringUtils { /// /// - 支持 snake_case、kebab-case、空格、特殊字符自动转为 camelCase /// - 已经是驼峰命名的字符串保持不变 + /// - PascalCase(首字母大写)转换为camelCase(首字母小写) /// - 数字开头自动加前缀 n /// - 空字符串返回 'property' /// @@ -104,33 +116,32 @@ class StringUtils { /// StringUtils.toDartPropertyName('user-id'); // userId /// StringUtils.toDartPropertyName('1st_field'); // n1stField /// StringUtils.toDartPropertyName('classCadreId'); // classCadreId + /// StringUtils.toDartPropertyName('PageIndex'); // pageIndex /// StringUtils.toDartPropertyName(''); // property /// ``` static String toDartPropertyName(String propName) { - // 如果已经是驼峰命名(没有下划线且首字母小写),直接返回 + // 如果已经是camelCase命名(没有下划线且首字母小写),直接返回 if (RegExp(r'^[a-z][a-zA-Z0-9]*$').hasMatch(propName)) { return propName; } - + // PascalCase 直接转 camelCase + if (RegExp(r'^[A-Z][a-zA-Z0-9]*$').hasMatch(propName)) { + return propName[0].toLowerCase() + propName.substring(1); + } // 处理特殊字符和数字开头的情况 String result = propName; - // 如果以数字开头,添加前缀 if (RegExp(r'^[0-9]').hasMatch(result)) { result = 'n$result'; } - // 替换特殊字符为下划线 result = result.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_'); - // 转换为camelCase result = toCamelCase(result); - // 确保不为空 if (result.isEmpty) { result = 'property'; } - return result; } @@ -146,7 +157,7 @@ class StringUtils { // 移除可能引起语法错误的特殊字符 cleaned = cleaned - .replaceAll(RegExp(r'[^\w\s\u4e00-\u9fa5()(),,.。::;;!!??_/\\-]'), '') + .replaceAll(RegExp(r'[^\w\s\u4e00-\u9fa5,,.。::;;!!??_/\\-]'), '') .replaceAll(RegExp(r'\s+'), ' ') .trim(); diff --git a/test_function_name.dart b/test_function_name.dart new file mode 100644 index 0000000..c537341 --- /dev/null +++ b/test_function_name.dart @@ -0,0 +1,21 @@ +import 'lib/utils/string_utils.dart'; + +void main() { + print('Testing function name generation:'); + print( + 'GetClassesTaskChecklistUsers -> ${StringUtils.toCamelCase('GetClassesTaskChecklistUsers')}'); + print('GetUserInfo -> ${StringUtils.toCamelCase('GetUserInfo')}'); + print('CreateTask -> ${StringUtils.toCamelCase('CreateTask')}'); + print('UpdateUserProfile -> ${StringUtils.toCamelCase('UpdateUserProfile')}'); + print('DeleteTaskById -> ${StringUtils.toCamelCase('DeleteTaskById')}'); + + print('\nTesting existing camelCase:'); + print( + 'getClassesTaskChecklistUsers -> ${StringUtils.toCamelCase('getClassesTaskChecklistUsers')}'); + print('getUserInfo -> ${StringUtils.toCamelCase('getUserInfo')}'); + + print('\nTesting snake_case:'); + print( + 'get_classes_task_checklist_users -> ${StringUtils.toCamelCase('get_classes_task_checklist_users')}'); + print('get_user_info -> ${StringUtils.toCamelCase('get_user_info')}'); +} diff --git a/test_property_name.dart b/test_property_name.dart index 3af205a..8b6cfbb 100644 --- a/test_property_name.dart +++ b/test_property_name.dart @@ -1,7 +1,6 @@ import 'lib/utils/string_utils.dart'; void main() { - // 测试属性名转换 print('Testing property name conversion:'); print('classCadreId -> ${StringUtils.toDartPropertyName('classCadreId')}'); print('meetingTitle -> ${StringUtils.toDartPropertyName('meetingTitle')}'); @@ -11,10 +10,67 @@ void main() { print( 'sunTaskFileResults -> ${StringUtils.toDartPropertyName('sunTaskFileResults')}'); - // 测试下划线格式 print('\nTesting snake_case conversion:'); print( 'class_cadre_id -> ${StringUtils.toDartPropertyName('class_cadre_id')}'); print('meeting_title -> ${StringUtils.toDartPropertyName('meeting_title')}'); print('task_info -> ${StringUtils.toDartPropertyName('task_info')}'); + + print('\nTesting problematic field names:'); + print('PageIndex -> ${StringUtils.toDartPropertyName('PageIndex')}'); + print('ProblemTitle -> ${StringUtils.toDartPropertyName('ProblemTitle')}'); + print('ProblemObj -> ${StringUtils.toDartPropertyName('ProblemObj')}'); + print( + 'ProblemPhenomenon -> ${StringUtils.toDartPropertyName('ProblemPhenomenon')}'); + print('ClassesId -> ${StringUtils.toDartPropertyName('ClassesId')}'); + print( + 'ProblemTaskType -> ${StringUtils.toDartPropertyName('ProblemTaskType')}'); + print('PageSize -> ${StringUtils.toDartPropertyName('PageSize')}'); + + print('\nTesting parameter name conversion:'); + print('api-version -> ${StringUtils.toDartPropertyName('api-version')}'); + print('user-id -> ${StringUtils.toDartPropertyName('user-id')}'); + print('file_name -> ${StringUtils.toDartPropertyName('file_name')}'); + print('with space -> ${StringUtils.toDartPropertyName('with space')}'); + + print('\nTesting kebab-case conversion:'); + print('api-version -> ${StringUtils.toDartPropertyName('api-version')}'); + print('user-id -> ${StringUtils.toDartPropertyName('user-id')}'); + print('page-size -> ${StringUtils.toDartPropertyName('page-size')}'); + print('to-camel-case -> ${StringUtils.toDartPropertyName('to-camel-case')}'); + + print('\nTesting tag names:'); + print( + 'Follow Manager -> ${StringUtils.toDartPropertyName('Follow Manager')}'); + print('Health Check -> ${StringUtils.toDartPropertyName('Health Check')}'); + print( + 'Mobile Manager -> ${StringUtils.toDartPropertyName('Mobile Manager')}'); + print('My Info -> ${StringUtils.toDartPropertyName('My Info')}'); + print( + 'Task Class Cadre Meeting -> ${StringUtils.toDartPropertyName('Task Class Cadre Meeting')}'); + print( + 'Task Class Meeting -> ${StringUtils.toDartPropertyName('Task Class Meeting')}'); + print( + 'Task Coach Sub -> ${StringUtils.toDartPropertyName('Task Coach Sub')}'); + print('Task Cultural -> ${StringUtils.toDartPropertyName('Task Cultural')}'); + print( + 'Task Data Collect -> ${StringUtils.toDartPropertyName('Task Data Collect')}'); + print('Task Follow -> ${StringUtils.toDartPropertyName('Task Follow')}'); + print('Task Info -> ${StringUtils.toDartPropertyName('Task Info')}'); + print('Task Meeting -> ${StringUtils.toDartPropertyName('Task Meeting')}'); + print('Task Other -> ${StringUtils.toDartPropertyName('Task Other')}'); + print('Task Solution -> ${StringUtils.toDartPropertyName('Task Solution')}'); + print('Task Spot -> ${StringUtils.toDartPropertyName('Task Spot')}'); + print( + 'Task Summarize -> ${StringUtils.toDartPropertyName('Task Summarize')}'); + print('Task Talk -> ${StringUtils.toDartPropertyName('Task Talk')}'); + print( + 'Task Teacher Behavior -> ${StringUtils.toDartPropertyName('Task Teacher Behavior')}'); + print( + 'Task Teacher Talk -> ${StringUtils.toDartPropertyName('Task Teacher Talk')}'); + + print('\nTesting comment cleaning:'); + print('部长新增工作任务指标(会删除所有管理的班级任务指标)-删除所有管理的学习官的通用任务指标'); + print( + 'Cleaned: ${StringUtils.cleanDescription('部长新增工作任务指标(会删除所有管理的班级任务指标)-删除所有管理的学习官的通用任务指标')}'); } diff --git a/tests/string_utils_test.dart b/tests/string_utils_test.dart index 1cd5957..b88e7b4 100644 --- a/tests/string_utils_test.dart +++ b/tests/string_utils_test.dart @@ -32,6 +32,23 @@ void main() { expect(StringUtils.toCamelCase('api_version'), 'apiVersion'); }); + test('converts PascalCase to camelCase', () { + expect(StringUtils.toCamelCase('GetClassesTaskChecklistUsers'), + 'getClassesTaskChecklistUsers'); + expect(StringUtils.toCamelCase('GetUserInfo'), 'getUserInfo'); + expect(StringUtils.toCamelCase('CreateTask'), 'createTask'); + expect( + StringUtils.toCamelCase('UpdateUserProfile'), 'updateUserProfile'); + expect(StringUtils.toCamelCase('DeleteTaskById'), 'deleteTaskById'); + }); + + test('preserves existing camelCase', () { + expect(StringUtils.toCamelCase('getClassesTaskChecklistUsers'), + 'getClassesTaskChecklistUsers'); + expect(StringUtils.toCamelCase('getUserInfo'), 'getUserInfo'); + expect(StringUtils.toCamelCase('createTask'), 'createTask'); + }); + test('handles single word', () { expect(StringUtils.toCamelCase('user'), 'user'); expect(StringUtils.toCamelCase(''), '');