swagger_generator_flutter/AUGMENT_CODE_GENERATION_STA...

11 KiB
Raw Blame History

Augment 代码生成规范

基于 OpenAPI 3.0 标准的 Flutter API 代码生成规范

📋 核心原则

1. OpenAPI 3.0 标准优先

  • 严格遵循 OpenAPI 3.0 规范
  • swagger.json 是唯一真实来源
  • 不进行主观推断或猜测
  • 有问题与服务器端沟通完善文档

2. 类型安全第一

  • 所有类型必须从 schema 定义中提取
  • 禁止硬编码类型映射
  • 使用强类型,避免 dynamic
  • 严格的可空性控制

3. 一致性保证

  • 统一的命名规范
  • 统一的文件结构
  • 统一的代码风格
  • 统一的注释格式

🏗️ 项目结构规范

目录结构

generator/
├── api/                    # API 接口文件
│   ├── api_client.dart    # 主 API 客户端
│   ├── login_api.dart     # 按 tag 分组的 API
│   └── ...
├── api_models/            # 数据模型
│   ├── index.dart         # 统一导出文件
│   ├── user_result.dart   # 具体模型类
│   └── ...
├── api_paths.dart         # API 路径常量
├── build.yaml            # 构建配置
└── pubspec.yaml          # 依赖配置

文件命名规范

  • API 文件: {tag_name}_api.dart (snake_case)
  • 模型文件: {schema_name}.dart (snake_case)
  • 参数文件: {operation_id}_parameters.dart
  • 类名: PascalCase
  • 方法名: camelCase
  • 常量: UPPER_SNAKE_CASE

🔧 代码生成规范

1. API 接口生成

基本结构

// {Tag} API 接口定义
// 基于 Swagger API 文档: {swagger_url}
// 由 xy_swagger_generator by max 生成
// Copyright (C) 2025 YuanXuan. All rights reserved.

import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
import 'package:learning_officer_oa/common/models/common/base_result.dart';

// 按需导入分页类型
import 'package:learning_officer_oa/common/models/common/base_page_result.dart';

part '{tag_name}_api.g.dart';

/// {Tag} API 接口
/// 负责处理 {Tag} 相关的接口
@RestApi(parser: Parser.JsonSerializable)
abstract class {Tag}Api {
  factory {Tag}Api(Dio dio, {String? baseUrl}) = _{Tag}Api;
}

方法生成规则

/// {summary}
@{HTTP_METHOD}('{path}')
Future<{ReturnType}> {methodName}(
  // 路径参数
  @Path('{param}') {Type} param,
  // 查询参数
  @Query('{param}') {Type}? param,
  // 请求体(仅当 swagger 中明确定义时)
  @Body() {Type} request
);

2. 返回类型规范

类型提取优先级

  1. 从 responses.200.content.application/json.schema 提取
  2. 从 responses.200.content.text/plain.schema 提取
  3. 特殊处理: 健康检查接口返回 BaseResult<void>
  4. 默认: BaseResult<Map<String, dynamic>>

分页类型判断

// 当返回类型包含分页结构时使用 BasePageResult
BasePageResult<{ItemType}>

// 判断依据:
// 1. schema 中包含 pageIndex, pageSize, totalCount 等字段
// 2. 查询参数中包含分页参数 (page, size, limit 等)

3. 请求体生成规范

添加 @Body() 的条件

// 仅在以下情况添加 @Body() 参数:
// 1. requestBody 在 swagger 中明确定义
// 2. parameters 中存在 in: "body" 的参数
// 3. 其他情况一律不添加

请求体类型提取

// 优先级:
// 1. requestBody.content.application/json.schema
// 2. requestBody.content.text/plain.schema
// 3. 默认: Map<String, dynamic>

📝 数据模型生成规范

1. 模型类结构

基本模板

// {schemaName} 模型定义
// 基于 Swagger API 文档: {swagger_url}
// 由 xy_swagger_generator by max 生成
// Copyright (C) 2025 YuanXuan. All rights reserved.

import 'package:json_annotation/json_annotation.dart';
import 'index.dart';

part '{schema_name}.g.dart';

@JsonSerializable(checked: true, includeIfNull: false)
class {SchemaName} {
  // 属性定义

  const {SchemaName}({
    // 构造函数参数
  });

  factory {SchemaName}.fromJson(Map<String, dynamic> json) =>
      _${SchemaName}FromJson(json);

  Map<String, dynamic> toJson() => _${SchemaName}ToJson(this);
}

2. 属性生成规则

可空性判断

// 严格按照 OpenAPI 规范:
// 1. 有 "nullable": true -> 可空类型 (Type?)
// 2. 没有 "nullable": true -> 非空类型 (Type)
// 3. 忽略 required 字段,只看 nullable

final String name;        // 非空
final String? nickname;   // 可空 (nullable: true)

类型映射

// OpenAPI -> Dart 类型映射
"string"    -> String
"integer"   -> int
"number"    -> double
"boolean"   -> bool
"array"     -> List<T>
"object"    -> Map<String, dynamic> 或具体类型
"$ref"      -> 引用的具体类型

构造函数规则

const {ClassName}({
  required this.nonNullableField,  // 非空字段必须 required
  this.nullableField,              // 可空字段不需要 required
});

3. 导入管理

按需导入原则

// 只导入实际使用的类型
// 分页相关
import 'package:learning_officer_oa/common/models/common/base_page_result.dart';

// 基础响应
import 'package:learning_officer_oa/common/models/common/base_result.dart';

// 模型类型
import '../../api_models/{model_name}.dart';

🚫 禁止事项

1. 硬编码推断

// ❌ 禁止基于路径关键词推断类型
if (path.contains('login')) return 'UserLoginResult';

// ❌ 禁止基于 tag 推断类型
if (tag.contains('task')) return 'TaskInfoResult';

// ❌ 禁止基于操作推断请求体
if (method == 'POST') addBody();

2. 不存在的类型

// ❌ 禁止生成 swagger 中不存在的类型
Future<BaseResult<TaskInfoResult>>  // TaskInfoResult 不存在

// ✅ 使用通用类型或实际存在的类型
Future<BaseResult<Map<String, dynamic>>>
Future<BaseResult<ActualExistingType>>

3. 主观判断

// ❌ 禁止主观添加参数
@Body() Map<String, dynamic> request  // swagger 中没有定义

// ❌ 禁止主观修改类型
List<dynamic> -> List<SpecificType>   // 没有明确的 items schema

质量保证

1. 生成前检查

  • 验证 swagger.json 格式正确性
  • 检查所有 $ref 引用完整性
  • 确认 components/schemas 定义完整

2. 生成后验证

  • 所有生成的类型在 swagger 中都有定义
  • 没有硬编码的类型映射
  • 导入语句按需生成
  • 代码通过 dart analyze 检查

3. 错误处理

// 当 swagger 定义不完整时的处理策略:
// 1. 记录警告日志
// 2. 使用安全的默认类型
// 3. 提示完善 swagger 文档
// 4. 不进行主观推断

📞 沟通机制

文档问题反馈

  1. 发现 swagger 定义缺失 -> 联系后端完善
  2. 类型定义不明确 -> 要求明确 schema
  3. 响应结构不一致 -> 统一响应格式
  4. 参数定义缺失 -> 补充参数说明

版本管理

  • swagger.json 版本控制
  • 生成代码版本标记
  • 变更日志记录
  • 向后兼容性检查

🛠️ 工具配置规范

1. 生成器配置

# pubspec.yaml
dependencies:
  dio: ^5.0.0
  retrofit: ^4.0.0
  json_annotation: ^4.8.0

dev_dependencies:
  build_runner: ^2.3.0
  retrofit_generator: ^8.0.0
  json_serializable: ^6.6.0

2. 构建配置

# build.yaml
targets:
  $default:
    builders:
      json_serializable:
        options:
          checked: true
          include_if_null: false
          explicit_to_json: true

3. 分析配置

# analysis_options.yaml
analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false

linter:
  rules:
    - prefer_const_constructors
    - prefer_final_fields
    - avoid_dynamic_calls

📚 最佳实践示例

1. 标准 API 接口

/// 用户登录
@POST('/api/v1/Login/userLogin')
Future<BaseResult<UserLoginResult>> userLogin(
  @Body() LoginRequest request
);

/// 获取用户列表(分页)
@GET('/api/v1/User/GetUserList')
Future<BasePageResult<UserResult>> getUserList(
  @Query('page') int page,
  @Query('size') int size,
  @Query('keyword') String? keyword
);

/// 健康检查
@GET('/health')
Future<BaseResult<void>> healthCheck();

/// 无明确 schema 的接口
@POST('/api/v1/Action/DoSomething')
Future<BaseResult<Map<String, dynamic>>> doSomething();

2. 标准数据模型

@JsonSerializable(checked: true, includeIfNull: false)
class UserResult {
  /// 用户ID
  final int id;

  /// 用户名
  final String username;

  /// 昵称(可空)
  final String? nickname;

  /// 头像URL可空
  final String? avatarUrl;

  /// 是否激活
  final bool isActive;

  const UserResult({
    required this.id,
    required this.username,
    this.nickname,
    this.avatarUrl,
    required this.isActive,
  });

  factory UserResult.fromJson(Map<String, dynamic> json) =>
      _$UserResultFromJson(json);

  Map<String, dynamic> toJson() => _$UserResultToJson(this);
}

3. 参数类生成

@JsonSerializable(checked: true, includeIfNull: false)
class GetUserListParameters {
  final int page;
  final int size;
  final String? keyword;

  const GetUserListParameters({
    required this.page,
    required this.size,
    this.keyword,
  });

  factory GetUserListParameters.fromJson(Map<String, dynamic> json) =>
      _$GetUserListParametersFromJson(json);

  Map<String, dynamic> toJson() => _$GetUserListParametersToJson(this);
}

🔍 代码审查清单

生成代码检查项

  • 所有类型都在 swagger.json 中有定义
  • 没有硬编码的类型推断
  • 可空性严格按照 nullable 字段
  • 导入语句按需生成
  • 方法参数与 swagger 定义一致
  • 返回类型正确提取
  • 注释信息完整
  • 代码格式规范

swagger.json 检查项

  • 所有接口都有明确的 responses 定义
  • 所有 schema 都有完整的属性定义
  • 所有 $ref 引用都存在
  • 参数定义完整name, in, schema
  • requestBody 定义明确
  • 版本信息正确

📖 参考资源

官方文档

项目相关


最后更新: 2025-01-24 版本: v2.0 维护者: Augment Team