Template
Core 基础设施:Flavor 三包体系、Drift+SQLCipher 加密数据库 7 拦截器 Dio 网络栈、CrashReporter 崩溃日志、Sealed Failure 错误体系 5 色板主题系统、Auth 认证骨架(Clean Architecture)、Dev Panel、Mock Adapter 通过验证:flutter analyze(0 errors)、flutter test(全绿)、staging APK 74.8MB
54 lines
1.6 KiB
Dart
54 lines
1.6 KiB
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
import 'package:sunny_mochi/core/error/failures.dart';
|
|
import 'package:sunny_mochi/core/network/response_code.dart';
|
|
|
|
part 'api_response.freezed.dart';
|
|
part 'api_response.g.dart';
|
|
|
|
@Freezed(genericArgumentFactories: true)
|
|
abstract class ApiResponse<T> with _$ApiResponse<T> {
|
|
const factory ApiResponse({
|
|
@JsonKey(name: 'code') required String retCode,
|
|
@Default('') @JsonKey(name: 'msg') String retMsg,
|
|
@JsonKey(name: 'data') T? retData,
|
|
}) = _ApiResponse;
|
|
|
|
factory ApiResponse.fromJson(
|
|
Map<String, dynamic> json,
|
|
T Function(Object?) fromJsonT,
|
|
) => _$ApiResponseFromJson(json, fromJsonT);
|
|
}
|
|
|
|
extension ApiResponseX<T> on ApiResponse<T> {
|
|
bool get isSuccess => ResponseCode.isSuccess(retCode);
|
|
|
|
bool get isTokenExpired => ResponseCode.isUnauthorized(retCode);
|
|
|
|
T? unwrap() {
|
|
if (!isSuccess) throw ServerFailure(message: retMsg, code: retCode);
|
|
return retData;
|
|
}
|
|
|
|
void unwrapVoid() {
|
|
if (!isSuccess) throw ServerFailure(message: retMsg, code: retCode);
|
|
}
|
|
|
|
T unwrapRequired([String errorMsg = '响应数据为空']) {
|
|
final data = unwrap();
|
|
if (data == null) throw ServerFailure(message: errorMsg);
|
|
return data;
|
|
}
|
|
}
|
|
|
|
/// Datasource 层 envelope 解析入口。
|
|
///
|
|
/// 用法:`parseEnvelope(resp.data, (j) => MyModel.fromJson(j))?.unwrap()`
|
|
ApiResponse<T> parseEnvelope<T>(
|
|
Map<String, dynamic>? body,
|
|
T Function(Object?) fromJsonT, {
|
|
String emptyMsg = '响应为空',
|
|
}) {
|
|
if (body == null) throw ServerFailure(message: emptyMsg);
|
|
return ApiResponse.fromJson(body, fromJsonT);
|
|
}
|