diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..af1f32d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,76 @@ +# 1. 继承 Lint 规则集 (必选其一) +# -------------------------------------------------------------------------- +# 强烈推荐!根据你的项目类型选择一个 Lint 规则集作为起点。 +# 这能大大减少手动配置的工作量,并与社区最佳实践保持一致。 +# Linter 规则 + +# https://dart.ac.cn/tools/linter-rules + +# 如果是 Flutter 项目,推荐使用: +include: package:flutter_lints/flutter.yaml + +# 如果是纯 Dart 项目,推荐使用: +# include: package:lints/recommended.yaml + +# 2. 配置分析器 (必选) +# -------------------------------------------------------------------------- +# 分析器用于检查代码的语法和潜在问题。 +# 强烈建议启用所有规则,以确保代码质量和一致性。 + +analyzer: + errors: + require_trailing_commas: ignore + # 排除不想被分析的文件或目录。 + # 对于由代码生成工具(如 json_serializable, freezed)生成的 *.g.dart 文件, + # 强烈建议排除,因为你通常不需要对它们进行 Lint 检查。 + exclude: + - '**/*.g.dart' # 排除所有以 .g.dart 结尾的文件 + - 'lib/generated/**' # 排除 lib/generated/ 目录下的所有文件 (如果你的生成文件都在这里) + - 'build/**' # 排除 Flutter/Dart 构建输出目录 + + +# 3. 配置 Lint 规则 +# -------------------------------------------------------------------------- +linter: + # 在此处启用或禁用特定的 Lint 规则。 + # `include` 中的规则集已经包含了大部分常用规则,这里可以进行微调。 + rules: + # 常用且推荐启用的规则 (即使默认集没有包含,也建议手动添加) + - avoid_empty_else # 避免空的 else 块 + - avoid_print # 在生产代码中避免使用 print (可根据项目需求启用/禁用) + - avoid_relative_lib_imports # 避免从 'lib/' 相对导入 + # - avoid_return_and_type_annotation # 避免冗余的返回类型注解 + - curly_braces_in_flow_control_structures # 控制流语句强制使用大括号 + - empty_catches # 避免空的 catch 块 + - empty_constructor_bodies # 避免空的构造函数体 + - empty_statements # 避免空的语句 + - file_names # 文件名使用小写下划线命名 (my_file.dart) + - prefer_const_constructors # 尽可能使用 const 构造函数 + - prefer_const_declarations # 尽可能使用 const 声明 + - prefer_const_literals_to_create_immutables # 尽可能使用 const 创建不可变集合 + # - prefer_single_quotes # 优先使用单引号 (或 prefer_double_quotes) + - prefer_final_fields # 类中的私有字段尽可能使用 final + - prefer_final_locals # 局部变量尽可能使用 final + # - prefer_for_elements_to_map_fromIterable # 优先使用 for 元素创建 Map + # - prefer_is_empty # 优先使用 .isEmpty + # - prefer_is_not_empty # 优先使用 .isNotEmpty + - unnecessary_new # Dart 2.0 后 new 关键字是可选的,推荐省略 + - unnecessary_this # 避免不必要的 this 关键字 + # - use_key_in_widget_constructors # Flutter Widget 构造函数中推荐使用 Key + + # 根据项目特性考虑启用的规则 (可能需要团队讨论) + # - annotate_overrides # 推荐:覆写方法添加 @override 注解 (如果 flutter_lints 已包含则无需重复) + # - lines_longer_than_80_chars # 强制行长 80 字符 (默认是警告,但通常较严格) + # - public_member_api_docs # 推荐:为公共 API 编写文档注释 (对库项目非常重要,应用项目可酌情) + - require_trailing_commas # 强制多行参数列表使用尾随逗号 (有助于格式化) + # - sort_constructors_first # 构造函数在类中声明在前 + # - sort_declarations_as_members # 类成员按字母顺序排序 + # - sort_pub_dependencies # pubspec.yaml 依赖按字母排序 + +# 4. 格式化器配置 +# -------------------------------------------------------------------------- +formatter: + # 设置 `dart format` 工具的行宽。 + # Dart 官方推荐 80,但许多团队会使用 100 或 120 以适应现代宽屏显示器。 + # 最重要的是整个团队**保持一致**。 + page_width: 80 diff --git a/bin/main.dart b/bin/main.dart index c57040c..2d10d1a 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -2,7 +2,7 @@ import 'dart:io'; -import '../lib/swagger_cli_new.dart'; +import 'package:swagger_generator_flutter/swagger_cli_new.dart'; /// Swagger CLI 工具主入口 /// diff --git a/lib/commands/base_command.dart b/lib/commands/base_command.dart index 01a6373..5d55f02 100644 --- a/lib/commands/base_command.dart +++ b/lib/commands/base_command.dart @@ -42,9 +42,8 @@ abstract class BaseCommand { print('选项:'); for (final option in options) { final short = option.shortName != null ? '-${option.shortName}, ' : ''; - final defaultValue = option.defaultValue != null - ? ' (默认: ${option.defaultValue})' - : ''; + final defaultValue = + option.defaultValue != null ? ' (默认: ${option.defaultValue})' : ''; print(' $short--${option.name} ${option.description}$defaultValue'); } } diff --git a/lib/commands/generate_command.dart b/lib/commands/generate_command.dart index ce5a48f..ecd464d 100644 --- a/lib/commands/generate_command.dart +++ b/lib/commands/generate_command.dart @@ -320,7 +320,6 @@ class GenerateCommand extends BaseCommand { // 生成文件头 buffer.writeln('// API 模型导出文件'); buffer.writeln('// 基于 Swagger API 文档: '); - buffer.writeln('// 自动生成于: ${DateTime.now().toString().substring(0, 10)} '); buffer.writeln('// 由 xy_swagger_generator by max 生成'); buffer.writeln('// Copyright (C) 2025 YuanXuan. All rights reserved.'); buffer.writeln(''); diff --git a/lib/generators/base_generator.dart b/lib/generators/base_generator.dart index 717375a..9b3d124 100644 --- a/lib/generators/base_generator.dart +++ b/lib/generators/base_generator.dart @@ -279,7 +279,7 @@ class GeneratorOptions { bool generateModels = false; bool generateDocs = false; bool useSimpleModels = false; - bool separateModelFiles = true; + const bool separateModelFiles = true; String modelsDirectory = 'models'; String outputDirectory = 'generator'; String endpointsFileName = 'api_paths.dart'; diff --git a/lib/generators/retrofit_api_generator.dart b/lib/generators/retrofit_api_generator.dart index 6f5834f..179ce1d 100644 --- a/lib/generators/retrofit_api_generator.dart +++ b/lib/generators/retrofit_api_generator.dart @@ -863,7 +863,7 @@ class RetrofitApiGenerator extends BaseGenerator { param.name.isNotEmpty ? param.name : 'request'), type: bodyType, annotation: useRetrofit ? '@Body()' : '', - required: true, + required: false, )); } @@ -877,7 +877,7 @@ class RetrofitApiGenerator extends BaseGenerator { name: 'request', type: bodyType, annotation: useRetrofit ? '@Body()' : '', - required: true, + required: false, )); } @@ -1010,7 +1010,7 @@ class RetrofitApiGenerator extends BaseGenerator { buffer.writeln(' ) async {'); // 构建请求路径 - var requestPath = cleanPath; + final requestPath = cleanPath; final pathParams = parameters.where((p) => p.annotation.contains('@Path')).toList(); @@ -1525,7 +1525,7 @@ class RetrofitApiGenerator extends BaseGenerator { buffer.writeln(''); // 生成参数实体类 - buffer.writeln('@JsonSerializable(checked: true, includeIfNull:false)'); + buffer.writeln('@JsonSerializable(checked: true, includeIfNull: false)'); buffer.writeln('class $className {'); // 生成属性 diff --git a/lib/utils/string_utils.dart b/lib/utils/string_utils.dart index 082e24f..f461494 100644 --- a/lib/utils/string_utils.dart +++ b/lib/utils/string_utils.dart @@ -265,8 +265,9 @@ class StringUtils { static String formatBytes(int bytes) { if (bytes < 1024) return '${bytes}B'; if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB'; - if (bytes < 1024 * 1024 * 1024) + if (bytes < 1024 * 1024 * 1024) { return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB'; + } return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB'; } @@ -344,10 +345,9 @@ class StringUtils { /// 生成文件头注释 static String generateFileHeader(String description, String source) { - final timestamp = DateTime.now().toLocal().toString().split(" ").first; + // final timestamp = DateTime.now().toLocal().toString().split(" ").first; return '''// $description // 基于 Swagger API 文档: -// 自动生成于: $timestamp // 由 xy_swagger_generator by max 生成 // Copyright (C) 2025 YuanXuan. All rights reserved. '''; diff --git a/lib/utils/type_validator.dart b/lib/utils/type_validator.dart index ad3b0b7..f34ad3d 100644 --- a/lib/utils/type_validator.dart +++ b/lib/utils/type_validator.dart @@ -72,7 +72,7 @@ class TypeValidator { if (property.type == PropertyType.reference) { if (property.reference == null || property.reference!.isEmpty) { errors.add( - ValidationError( + const ValidationError( field: 'reference', message: '引用类型缺少引用目标', severity: ErrorSeverity.error, @@ -217,7 +217,7 @@ class TypeValidator { // 基础语法检查 if (code.trim().isEmpty) { errors.add( - ValidationError( + const ValidationError( field: 'code', message: '生成的代码为空', severity: ErrorSeverity.error, @@ -228,7 +228,7 @@ class TypeValidator { // 检查括号匹配 if (!_isBalancedBrackets(code)) { errors.add( - ValidationError( + const ValidationError( field: 'syntax', message: '代码中存在不匹配的括号', severity: ErrorSeverity.error, @@ -241,7 +241,7 @@ class TypeValidator { case CodeType.model: if (!code.contains('class ') && !code.contains('enum ')) { errors.add( - ValidationError( + const ValidationError( field: 'content', message: '模型代码必须包含class或enum声明', severity: ErrorSeverity.error, @@ -252,7 +252,7 @@ class TypeValidator { case CodeType.endpoints: if (!code.contains('class ')) { errors.add( - ValidationError( + const ValidationError( field: 'content', message: '端点代码必须包含class声明', severity: ErrorSeverity.error, @@ -288,7 +288,7 @@ class TypeValidator { if (model.enumValues.isEmpty) { errors.add( - ValidationError( + const ValidationError( field: 'enumValues', message: '枚举类型必须包含至少一个值', severity: ErrorSeverity.error, @@ -311,7 +311,7 @@ class TypeValidator { final uniqueValues = model.enumValues.toSet(); if (uniqueValues.length != model.enumValues.length) { errors.add( - ValidationError( + const ValidationError( field: 'enumValues', message: '枚举值存在重复', severity: ErrorSeverity.error, diff --git a/pubspec.yaml b/pubspec.yaml index a3f5060..f0be349 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,8 @@ dependencies: json_annotation: ^4.8.1 http: ^1.1.0 + path: any + logging: any dev_dependencies: flutter_test: sdk: flutter @@ -23,5 +25,8 @@ dev_dependencies: json_serializable: ^6.7.1 test: ^1.24.0 + dio: any + retrofit: any + learning_officer_oa: any flutter: uses-material-design: true \ No newline at end of file diff --git a/run_swagger.sh b/run_swagger.sh index a22b842..115aa40 100755 --- a/run_swagger.sh +++ b/run_swagger.sh @@ -40,15 +40,23 @@ main() { case "$1" in all) dart run "$CLI_DART_FILE" generate --models --api --split-by-tags + dart format . + dart fix --apply ;; models) dart run "$CLI_DART_FILE" generate --models + dart format . + dart fix --apply ;; docs) dart run "$CLI_DART_FILE" generate --docs + dart format . + dart fix --apply ;; api) dart run "$CLI_DART_FILE" generate --api + dart format . + dart fix --apply ;; *) echo -e "${YELLOW}未知命令: $1${NC}" diff --git a/tests/models_test.dart b/tests/models_test.dart index edc9cfe..372c3d1 100644 --- a/tests/models_test.dart +++ b/tests/models_test.dart @@ -1,11 +1,11 @@ import 'package:test/test.dart'; -import '../lib/core/models.dart'; +import 'package:swagger_generator_flutter/core/models.dart'; void main() { group('ApiPath', () { test('creates ApiPath with required fields', () { - final path = ApiPath( + const path = ApiPath( path: '/api/users', method: HttpMethod.get, summary: 'Get users', @@ -29,7 +29,7 @@ void main() { test('creates ApiPath with all fields', () { final parameters = [ - ApiParameter( + const ApiParameter( name: 'id', location: ParameterLocation.path, required: true, @@ -39,7 +39,7 @@ void main() { ]; final responses = { - '200': ApiResponse( + '200': const ApiResponse( code: '200', description: 'Success', schema: {'type': 'object'}, @@ -47,7 +47,7 @@ void main() { ), }; - final requestBody = ApiRequestBody( + const requestBody = ApiRequestBody( description: 'User data', required: true, content: { @@ -77,7 +77,7 @@ void main() { group('ApiParameter', () { test('creates ApiParameter with required fields', () { - final param = ApiParameter( + const param = ApiParameter( name: 'id', location: ParameterLocation.path, required: true, @@ -93,7 +93,7 @@ void main() { }); test('creates ApiParameter with optional fields', () { - final param = ApiParameter( + const param = ApiParameter( name: 'page', location: ParameterLocation.query, required: false, @@ -136,7 +136,7 @@ void main() { group('ApiResponse', () { test('creates ApiResponse with required fields', () { - final response = ApiResponse( + const response = ApiResponse( code: '200', description: 'Success', schema: null, @@ -190,7 +190,7 @@ void main() { group('ApiRequestBody', () { test('creates ApiRequestBody with required fields', () { - final requestBody = ApiRequestBody( + const requestBody = ApiRequestBody( description: 'User data', required: true, content: null, @@ -244,7 +244,7 @@ void main() { group('ApiModel', () { test('creates ApiModel with required fields', () { - final model = ApiModel( + const model = ApiModel( name: 'User', description: 'User model', properties: {}, @@ -262,13 +262,13 @@ void main() { test('creates ApiModel with properties', () { final properties = { - 'id': ApiProperty( + 'id': const ApiProperty( name: 'id', type: PropertyType.integer, description: 'User ID', required: true, ), - 'name': ApiProperty( + 'name': const ApiProperty( name: 'name', type: PropertyType.string, description: 'User name', @@ -288,7 +288,7 @@ void main() { }); test('creates enum ApiModel', () { - final model = ApiModel( + const model = ApiModel( name: 'UserStatus', description: 'User status enum', properties: {}, @@ -344,7 +344,7 @@ void main() { group('ApiProperty', () { test('creates ApiProperty with required fields', () { - final property = ApiProperty( + const property = ApiProperty( name: 'id', type: PropertyType.integer, description: 'User ID', @@ -359,7 +359,7 @@ void main() { }); test('creates ApiProperty with optional fields', () { - final property = ApiProperty( + const property = ApiProperty( name: 'name', type: PropertyType.string, description: 'User name', @@ -497,7 +497,7 @@ void main() { group('SwaggerDocument', () { test('creates SwaggerDocument with required fields', () { - final document = SwaggerDocument( + const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test API description', @@ -578,7 +578,7 @@ void main() { group('ApiController', () { test('creates ApiController with required fields', () { - final controller = ApiController( + const controller = ApiController( name: 'UserController', description: 'User management controller', paths: [], @@ -591,7 +591,7 @@ void main() { test('creates ApiController with paths', () { final paths = [ - ApiPath( + const ApiPath( path: '/api/users', method: HttpMethod.get, summary: 'Get users', @@ -602,7 +602,7 @@ void main() { responses: {}, requestBody: null, ), - ApiPath( + const ApiPath( path: '/api/users/{id}', method: HttpMethod.post, summary: 'Create user', @@ -628,7 +628,7 @@ void main() { test('creates ApiController from paths', () { final paths = [ - ApiPath( + const ApiPath( path: '/api/users', method: HttpMethod.get, summary: 'Get users', diff --git a/tests/string_utils_test.dart b/tests/string_utils_test.dart index b88e7b4..f1b6638 100644 --- a/tests/string_utils_test.dart +++ b/tests/string_utils_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import '../lib/utils/string_utils.dart'; +import 'package:swagger_generator_flutter/utils/string_utils.dart'; void main() { group('StringUtils', () { @@ -211,10 +211,10 @@ void main() { group('formatDuration', () { test('formats duration correctly', () { - expect( - StringUtils.formatDuration(Duration(milliseconds: 500)), '500毫秒'); - expect(StringUtils.formatDuration(Duration(seconds: 1)), '1.00秒'); - expect(StringUtils.formatDuration(Duration(seconds: 2)), '2.00秒'); + expect(StringUtils.formatDuration(const Duration(milliseconds: 500)), + '500毫秒'); + expect(StringUtils.formatDuration(const Duration(seconds: 1)), '1.00秒'); + expect(StringUtils.formatDuration(const Duration(seconds: 2)), '2.00秒'); }); }); });