Merge remote-tracking branch 'origin/dev' into cody/netwrok_SDK
# Conflicts: # apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart # apps/im_app/lib/features/login/presentation/login_view_model.dart 修复逻辑漏洞,性能优化
This commit is contained in:
@@ -4,17 +4,50 @@ import 'package:networks_sdk/src/annotations/api_request.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'package:build/build.dart';
|
||||
|
||||
/// @JsonKey 检测器(用于读取字段的 JSON 键名映射)
|
||||
const _jsonKeyChecker = TypeChecker.fromUrl(
|
||||
'package:json_annotation/src/json_key.dart#JsonKey',
|
||||
);
|
||||
|
||||
/// @ApiRequest 代码生成器
|
||||
///
|
||||
/// 为标注了 `@ApiRequest` 的类自动生成 mixin,提供:
|
||||
/// - `path`, `method`, `requestType`, `includeToken` 协议实现
|
||||
/// - 自动注册响应类型的 `fromJson`(在 `parameters` getter 中触发,
|
||||
/// 保证首次请求前完成注册,无需手动调用 `registerApiResponses()`)
|
||||
/// - `toJson()` — 从类的声明字段自动生成,只序列化自身字段,
|
||||
/// 不含 ApiRequestable 的继承属性,避免递归
|
||||
/// - 自动注册响应类型的 `fromJson`(在 `parameters` getter 中触发)
|
||||
///
|
||||
/// 生成的 mixin 命名规则:`_$<ClassName>Api`
|
||||
/// 支持 `@JsonKey(name: '...')` 字段重命名。
|
||||
/// 如有 `@JsonKey(includeToJson: false)` 则跳过该字段。
|
||||
///
|
||||
/// ## 使用模式
|
||||
///
|
||||
/// Request 类只需 `@ApiRequest` 注解,无需 `@JsonSerializable`:
|
||||
///
|
||||
/// ```dart
|
||||
/// @ApiRequest(
|
||||
/// path: ApiPaths.authLogin,
|
||||
/// method: HttpMethod.post,
|
||||
/// responseType: LoginData,
|
||||
/// requestType: ApiRequestType.login,
|
||||
/// )
|
||||
/// class LoginRequest extends ApiRequestable<LoginData>
|
||||
/// with _$LoginRequestApi {
|
||||
/// final String email;
|
||||
/// final String password;
|
||||
///
|
||||
/// LoginRequest({required this.email, required this.password});
|
||||
/// // 完毕!toJson / path / method 全部由 mixin 自动生成
|
||||
/// // Response 的 fromJson 在 parameters getter 中自动注册
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## mixin 命名规则
|
||||
///
|
||||
/// `_$<ClassName>Api`
|
||||
///
|
||||
/// ## 生成示例
|
||||
///
|
||||
/// 示例输出:
|
||||
/// ```dart
|
||||
/// mixin _$LoginRequestApi on ApiRequestable<LoginData> {
|
||||
/// @override String get path => '/auth/login';
|
||||
@@ -22,17 +55,29 @@ import 'package:build/build.dart';
|
||||
/// @override ApiRequestType get requestType => ApiRequestType.login;
|
||||
/// @override bool get includeToken => false;
|
||||
/// @override
|
||||
/// Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
/// 'email': (this as LoginRequest).email,
|
||||
/// 'password': (this as LoginRequest).password,
|
||||
/// };
|
||||
/// @override
|
||||
/// Map<String, dynamic>? get parameters {
|
||||
/// registerResponse<LoginData>(LoginData.fromJson);
|
||||
/// return super.parameters;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
class ApiRequestGenerator extends GeneratorForAnnotation<ApiRequest>
|
||||
{
|
||||
///
|
||||
/// ## Upload 等特殊请求
|
||||
///
|
||||
/// 如需自定义 toJson(如 upload 返回空 map),在类中 override 即可,
|
||||
/// 类的 override 优先于 mixin。
|
||||
class ApiRequestGenerator extends GeneratorForAnnotation<ApiRequest> {
|
||||
@override
|
||||
String generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep,)
|
||||
{
|
||||
String generateForAnnotatedElement(
|
||||
Element element,
|
||||
ConstantReader annotation,
|
||||
BuildStep buildStep,
|
||||
) {
|
||||
if (element is! ClassElement) {
|
||||
throw InvalidGenerationSourceError(
|
||||
'@ApiRequest can only be applied to classes.',
|
||||
@@ -40,7 +85,7 @@ class ApiRequestGenerator extends GeneratorForAnnotation<ApiRequest>
|
||||
);
|
||||
}
|
||||
|
||||
final className = element.name;
|
||||
final className = element.name!;
|
||||
final path = annotation.read('path').stringValue;
|
||||
|
||||
// 读取 HttpMethod 枚举值
|
||||
@@ -68,6 +113,9 @@ class ApiRequestGenerator extends GeneratorForAnnotation<ApiRequest>
|
||||
includeToken = requestTypeName != 'login';
|
||||
}
|
||||
|
||||
// 从类的声明字段生成 toJson()
|
||||
final toJsonBody = _buildToJsonBody(element, className);
|
||||
|
||||
return '''
|
||||
/// Generated by @ApiRequest for [$className]
|
||||
mixin _\$${className}Api on ApiRequestable<$responseTypeName> {
|
||||
@@ -80,6 +128,8 @@ mixin _\$${className}Api on ApiRequestable<$responseTypeName> {
|
||||
@override
|
||||
bool get includeToken => $includeToken;
|
||||
@override
|
||||
Map<String, dynamic> toJson() => $toJsonBody;
|
||||
@override
|
||||
Map<String, dynamic>? get parameters {
|
||||
registerResponse<$responseTypeName>($responseTypeName.fromJson);
|
||||
return super.parameters;
|
||||
@@ -88,6 +138,46 @@ mixin _\$${className}Api on ApiRequestable<$responseTypeName> {
|
||||
''';
|
||||
}
|
||||
|
||||
/// 从类的声明字段构建 toJson() 方法体
|
||||
///
|
||||
/// 只读取类自身声明的实例字段(非 static、非 synthetic),
|
||||
/// 不含继承自 ApiRequestable 的属性,避免递归。
|
||||
/// 支持 @JsonKey(name: '...') 字段重命名,
|
||||
/// 以及 @JsonKey(includeToJson: false) 跳过字段。
|
||||
String _buildToJsonBody(ClassElement element, String className) {
|
||||
final fields = element.fields
|
||||
.where((f) => !f.isStatic && !f.isSynthetic)
|
||||
.toList();
|
||||
|
||||
if (fields.isEmpty) {
|
||||
return '<String, dynamic>{}';
|
||||
}
|
||||
|
||||
final entries = <String>[];
|
||||
for (final field in fields) {
|
||||
// 检查 @JsonKey 注解
|
||||
final jsonKeyAnnotation = _jsonKeyChecker.firstAnnotationOfExact(field);
|
||||
|
||||
// @JsonKey(includeToJson: false) → 跳过
|
||||
final includeToJson = jsonKeyAnnotation
|
||||
?.getField('includeToJson')
|
||||
?.toBoolValue();
|
||||
if (includeToJson == false) continue;
|
||||
|
||||
// JSON 键名:@JsonKey(name: '...') 或字段名
|
||||
final jsonName =
|
||||
jsonKeyAnnotation?.getField('name')?.toStringValue() ?? field.name;
|
||||
|
||||
entries.add("'$jsonName': (this as $className).${field.name}");
|
||||
}
|
||||
|
||||
if (entries.isEmpty) {
|
||||
return '<String, dynamic>{}';
|
||||
}
|
||||
|
||||
return '<String, dynamic>{${entries.join(', ')}}';
|
||||
}
|
||||
|
||||
/// 从 DartObject 提取枚举常量名称
|
||||
String _readEnumName(dynamic dartObject, String defaultValue) {
|
||||
final index = dartObject.getField('index')?.toIntValue();
|
||||
@@ -105,4 +195,4 @@ mixin _\$${className}Api on ApiRequestable<$responseTypeName> {
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user