极简接口定义和响应定义,支持更多的解析器
This commit is contained in:
@@ -886,8 +886,9 @@ flowchart TD
|
||||
│ ├── annotations/
|
||||
│ │ └── api_request.dart # @ApiRequest 注解定义
|
||||
│ ├── generator/
|
||||
│ │ ├── api_request_generator.dart # build_runner 代码生成器实现
|
||||
│ │ └── builder.dart # SharedPartBuilder 入口
|
||||
│ │ ├── api_request_generator.dart # Request mixin 生成器(toJson / path / method / fromJson 注册)
|
||||
│ │ ├── api_response_generator.dart # Response fromJson 生成器(从 @ApiRequest(responseType) 自动推导,递归嵌套类型)
|
||||
│ │ └── builder.dart # SharedPartBuilder 入口(两个生成器合并到同一 .g.dart)
|
||||
│ ├── data/
|
||||
│ │ ├── datasources/
|
||||
│ │ │ ├── http/
|
||||
@@ -2286,23 +2287,21 @@ extension APIRequestableExtension<T> on APIRequestable<T> {
|
||||
|
||||
<p>一个端点 = 一个文件(<code>data/remote/login_request.dart</code>),Response DTO + Request 放在同一文件中。</p>
|
||||
|
||||
<pre><code class="language-dart">import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
<pre><code class="language-dart">import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
part 'login_request.g.dart';
|
||||
|
||||
// ── Response DTO ──
|
||||
// ── Response DTO(纯 Dart 类,零注解,零样板)──
|
||||
// _$LoginResponseFromJson 由 ApiResponseGenerator 从 @ApiRequest(responseType: T) 自动推导生成
|
||||
|
||||
@JsonSerializable()
|
||||
class LoginData {
|
||||
class LoginResponse {
|
||||
final String token;
|
||||
@JsonKey(name: 'user_id')
|
||||
@JsonKey(name: 'user_id') // @JsonKey 由生成器读取,Response 类不需要 @JsonSerializable
|
||||
final String userId;
|
||||
final String email;
|
||||
|
||||
const LoginData({required this.token, required this.userId, required this.email});
|
||||
factory LoginData.fromJson(Map<String, dynamic> json) => _$LoginDataFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$LoginDataToJson(this);
|
||||
const LoginResponse({required this.token, required this.userId, required this.email});
|
||||
// 无 factory fromJson — 生成器在 .g.dart 中提供私有 _$LoginResponseFromJson
|
||||
|
||||
User toEntity() => User(id: userId, email: email); // DTO → Domain Entity
|
||||
}
|
||||
@@ -2313,23 +2312,23 @@ class LoginData {
|
||||
@ApiRequest(
|
||||
path: ApiPaths.authLogin, // 路径统一在 core/foundation/api_paths.dart 管理
|
||||
method: HttpMethod.post,
|
||||
responseType: LoginData,
|
||||
responseType: LoginResponse,
|
||||
requestType: ApiRequestType.login,
|
||||
)
|
||||
class LoginRequest extends ApiRequestable<LoginData> with _$LoginRequestApi {
|
||||
class LoginRequest extends ApiRequestable<LoginResponse> with _$LoginRequestApi {
|
||||
final String email;
|
||||
final String password;
|
||||
|
||||
LoginRequest({required this.email, required this.password});
|
||||
// 完毕!toJson 由 mixin 从类字段自动生成,fromJson 不需要(Request 永远手动构造)
|
||||
// 完毕!toJson 由 mixin 从类字段自动生成,fromJson 注册也全部自动处理
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<pre><code class="language-dart">// 使用 - 超级简单!
|
||||
final loginData = await apiClient.executeRequest(
|
||||
final loginResponse = await apiClient.executeRequest(
|
||||
LoginRequest(email: 'user@example.com', password: '123456'),
|
||||
);
|
||||
final user = loginData?.toEntity(); // DTO → Domain Entity
|
||||
final user = loginResponse?.toEntity(); // DTO → Domain Entity
|
||||
</code></pre>
|
||||
|
||||
<div style="background: #e8f5e9; padding: 20px; border-radius: 8px; border-left: 4px solid #388e3c; margin: 20px 0;">
|
||||
@@ -2359,8 +2358,8 @@ final user = loginData?.toEntity(); // DTO → Domain Entity
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@ApiRequest(当前方案)</strong></td>
|
||||
<td>字段 + 构造函数 + @ApiRequest</td>
|
||||
<td>path / method / requestType / includeToken / toJson / fromJson 注册</td>
|
||||
<td>字段 + 构造函数 + @ApiRequest<br/><em>(Response DTO:字段 + 构造函数,零注解)</em></td>
|
||||
<td>Request: path / method / requestType / includeToken / toJson / fromJson 注册<br/>Response: _$XFromJson 私有反序列化函数(按需递归生成嵌套类型)</td>
|
||||
<td><strong>极低</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -2368,10 +2367,10 @@ final user = loginData?.toEntity(); // DTO → Domain Entity
|
||||
|
||||
<p><strong>核心优势</strong>:</p>
|
||||
<ul>
|
||||
<li><strong>注解驱动</strong>:<code>@ApiRequest</code> 一个注解自动生成 mixin(含 toJson),无需 <code>@JsonSerializable</code></li>
|
||||
<li><strong>Request 零样板</strong>:<code>@ApiRequest</code> 一个注解生成 mixin(含 toJson),无需 <code>@JsonSerializable</code></li>
|
||||
<li><strong>Response 零注解</strong>:Response DTO 不需要 <code>@JsonSerializable</code>,不需要 <code>factory fromJson</code>,<code>_$XFromJson</code> 由生成器从 <code>@ApiRequest(responseType: T)</code> 自动推导;嵌套自定义类型递归生成,依赖关系自动处理</li>
|
||||
<li><strong>自动注册</strong>:fromJson 在首次请求时自动注册到全局注册表,无需手动 <code>registerApiResponses()</code></li>
|
||||
<li><strong>一个端点 = 一个文件</strong>:Response DTO + Request 放在同一文件,打开即看全貌</li>
|
||||
<li><strong>傻瓜式使用</strong>:使用者只需关注业务字段和注解配置</li>
|
||||
<li><strong>类型安全</strong>:<code>ApiRequestable<T></code> 泛型 + <code>responseType</code> 编译期检查</li>
|
||||
</ul>
|
||||
|
||||
@@ -2387,12 +2386,12 @@ final user = loginData?.toEntity(); // DTO → Domain Entity
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Swift</strong></td>
|
||||
<td><code>struct LoginRequest: APIRequestable { typealias Response = LoginData ... }</code></td>
|
||||
<td><code>struct LoginRequest: APIRequestable { typealias Response = LoginResponse ... }</code></td>
|
||||
<td>协议直接实现,最简洁</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Dart</strong></td>
|
||||
<td><code>@ApiRequest(...) class LoginRequest extends ApiRequestable<LoginData> with _$LoginRequestApi { ... }</code></td>
|
||||
<td><code>@ApiRequest(...) class LoginRequest extends ApiRequestable<LoginResponse> with _$LoginRequestApi { ... }</code></td>
|
||||
<td>注解 + 代码生成,接近 Swift 体验</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -2530,26 +2529,23 @@ melos run gen
|
||||
|
||||
<h6>4.5 更多使用示例</h6>
|
||||
|
||||
<p>所有示例遵循同一模式:<code>@ApiRequest</code> + <code>extends ApiRequestable<T> with _$XxxApi</code>。Request 类无需 <code>@JsonSerializable</code>。</p>
|
||||
<p>所有示例遵循同一模式:<code>@ApiRequest</code> + <code>extends ApiRequestable<T> with _$XxxApi</code>。Request 和 Response 均无需 <code>@JsonSerializable</code>,Response DTO 连 <code>factory fromJson</code> 也不需要。</p>
|
||||
|
||||
<p><strong>发送消息请求(POST + @JsonKey 字段重命名):</strong></p>
|
||||
<pre><code class="language-dart">// data/remote/send_message_request.dart
|
||||
|
||||
// ── Response DTO(仍用 @JsonSerializable)──
|
||||
@JsonSerializable()
|
||||
class SendMessageData {
|
||||
// ── Response DTO(纯 Dart 类,零注解,_$SendMessageResponseFromJson 由生成器自动提供)──
|
||||
class SendMessageResponse {
|
||||
@JsonKey(name: 'message_id')
|
||||
final String messageId;
|
||||
final int timestamp;
|
||||
|
||||
const SendMessageData({required this.messageId, required this.timestamp});
|
||||
factory SendMessageData.fromJson(Map<String, dynamic> json) =>
|
||||
_$SendMessageDataFromJson(json);
|
||||
const SendMessageResponse({required this.messageId, required this.timestamp});
|
||||
}
|
||||
|
||||
// ── Request(零样板)──
|
||||
@ApiRequest(path: ApiPaths.chatSendMessage, responseType: SendMessageData)
|
||||
class SendMessageRequest extends ApiRequestable<SendMessageData>
|
||||
@ApiRequest(path: ApiPaths.chatSendMessage, responseType: SendMessageResponse)
|
||||
class SendMessageRequest extends ApiRequestable<SendMessageResponse>
|
||||
with _$SendMessageRequestApi {
|
||||
@JsonKey(name: 'chat_id') // 生成器会读取,JSON 键名为 'chat_id'
|
||||
final String chatId;
|
||||
@@ -2563,23 +2559,21 @@ class SendMessageRequest extends ApiRequestable<SendMessageData>
|
||||
<p><strong>获取用户资料(GET,靠 token 标识当前用户,无需传参):</strong></p>
|
||||
<pre><code class="language-dart">// data/remote/get_profile_request.dart
|
||||
|
||||
@JsonSerializable(createToJson: false) // 只需反序列化
|
||||
class ProfileData {
|
||||
// ── Response DTO(纯 Dart 类,零注解,_$ProfileResponseFromJson 由生成器自动提供)──
|
||||
class ProfileResponse {
|
||||
@JsonKey(name: 'user_id')
|
||||
final String userId;
|
||||
final String email;
|
||||
final String? nickname;
|
||||
final String? avatar;
|
||||
|
||||
const ProfileData({required this.userId, required this.email, this.nickname, this.avatar});
|
||||
factory ProfileData.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProfileDataFromJson(json);
|
||||
const ProfileResponse({required this.userId, required this.email, this.nickname, this.avatar});
|
||||
|
||||
User toEntity() => User(id: userId, email: email, nickname: nickname, avatar: avatar);
|
||||
}
|
||||
|
||||
@ApiRequest(path: ApiPaths.userProfile, method: HttpMethod.get, responseType: ProfileData)
|
||||
class GetProfileRequest extends ApiRequestable<ProfileData>
|
||||
@ApiRequest(path: ApiPaths.userProfile, method: HttpMethod.get, responseType: ProfileResponse)
|
||||
class GetProfileRequest extends ApiRequestable<ProfileResponse>
|
||||
with _$GetProfileRequestApi {
|
||||
GetProfileRequest(); // 无参数 — toJson 自动生成空 map
|
||||
}
|
||||
@@ -2588,14 +2582,12 @@ class GetProfileRequest extends ApiRequestable<ProfileData>
|
||||
<p><strong>上传文件请求(FormData multipart):</strong></p>
|
||||
<pre><code class="language-dart">// data/remote/upload_file_request.dart
|
||||
|
||||
@JsonSerializable()
|
||||
// ── Response DTO(纯 Dart 类,零注解,_$UploadResultFromJson 由生成器自动提供)──
|
||||
class UploadResult {
|
||||
final String url;
|
||||
@JsonKey(name: 'file_id')
|
||||
final String fileId;
|
||||
const UploadResult({required this.url, required this.fileId});
|
||||
factory UploadResult.fromJson(Map<String, dynamic> json) =>
|
||||
_$UploadResultFromJson(json);
|
||||
}
|
||||
|
||||
@ApiRequest(
|
||||
@@ -2624,7 +2616,8 @@ class UploadFileRequest extends ApiRequestable<UploadResult>
|
||||
<div style="background: #fff3cd; padding: 15px; border-radius: 8px; border-left: 4px solid #ffc107; margin: 20px 0;">
|
||||
<p><strong>核心价值</strong></p>
|
||||
<ul style="margin-bottom: 0;">
|
||||
<li><strong>极简使用</strong>:字段 + 构造函数 + <code>@ApiRequest</code>(Request 无需 <code>@JsonSerializable</code>、无需 <code>fromJson</code>、无需手写 <code>toJson</code>)</li>
|
||||
<li><strong>Request 极简</strong>:字段 + 构造函数 + <code>@ApiRequest</code>(无需 <code>@JsonSerializable</code>、无需手写 <code>toJson</code>)</li>
|
||||
<li><strong>Response 零注解</strong>:Response DTO 是纯 Dart 类,无需 <code>@JsonSerializable</code>,无需 <code>factory fromJson</code>,<code>_$XFromJson</code> 由生成器从 <code>responseType</code> 自动推导</li>
|
||||
<li><strong>零维护</strong>:path / method / requestType / includeToken / toJson / fromJson 注册 全部自动生成</li>
|
||||
<li><strong>类型安全</strong>:泛型 <code>ApiRequestable<T></code> + <code>responseType</code> 编译期检查</li>
|
||||
<li><strong>一个端点 = 一个文件</strong>:Response DTO + Request 放在同一文件,打开即看全貌</li>
|
||||
@@ -2721,11 +2714,11 @@ class LoginViewModel extends _$LoginViewModel {
|
||||
→ LoggingInterceptor ← 请求/响应日志
|
||||
← request.decodeResponse(response) ← 自动解码
|
||||
← ApiResponseWrapper.fromJson ← 拆 { code, msg, data }
|
||||
← fromJsonRegistry[LoginData] ← 查注册表
|
||||
← LoginData.fromJson(data) ← 反序列化
|
||||
← LoginData(DTO)
|
||||
← fromJsonRegistry[LoginResponse] ← 查注册表
|
||||
← _$LoginResponseFromJson(data) ← 反序列化(生成的私有函数)
|
||||
← LoginResponse(DTO)
|
||||
→ onTokenUpdate(token) ← 回调写入 Token(内存 + 持久化)
|
||||
← loginData.toEntity() → User ← DTO → Domain Entity
|
||||
← loginResponse.toEntity() → User ← DTO → Domain Entity
|
||||
← User
|
||||
← state.copyWith(user: user) ← 更新状态
|
||||
View: ref.watch → 自动 rebuild ← UI 刷新
|
||||
@@ -5715,7 +5708,7 @@ class MessageLocalDataSource {
|
||||
class MessageRepositoryImpl implements MessageRepository {
|
||||
final NetworksSdkApi _client;
|
||||
|
||||
Future<SendMessageData?> sendMessage({
|
||||
Future<SendMessageResponse?> sendMessage({
|
||||
required String chatId,
|
||||
required String content,
|
||||
}) {
|
||||
@@ -5978,7 +5971,7 @@ flowchart LR
|
||||
<tr><th>层级</th><th>文件命名</th><th>类命名</th><th>示例</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>接口定义</td><td><code>{action}_request.dart</code></td><td>Request: <code>{Action}Request</code><br/>Response DTO: <code>{Action}Data</code></td><td><code>login_request.dart</code> → <code>LoginRequest</code> + <code>LoginData</code></td></tr>
|
||||
<tr><td>接口定义</td><td><code>{action}_request.dart</code></td><td>Request: <code>{Action}Request</code><br/>Response DTO: <code>{Action}Response</code></td><td><code>login_request.dart</code> → <code>LoginRequest</code> + <code>LoginResponse</code></td></tr>
|
||||
<tr><td>持久化 DTO</td><td><code>data/models/{entity}_dto.dart</code></td><td><code>{Entity}Dto</code></td><td><code>user_dto.dart</code> → <code>UserDto</code></td></tr>
|
||||
<tr><td>Repository 接口</td><td><code>domain/repositories/{module}_repository.dart</code></td><td><code>{Module}Repository</code></td><td><code>auth_repository.dart</code> → <code>AuthRepository</code></td></tr>
|
||||
<tr><td>Repository 实现</td><td><code>data/repositories/{module}_repository_impl.dart</code></td><td><code>{Module}RepositoryImpl</code></td><td><code>auth_repository_impl.dart</code> → <code>AuthRepositoryImpl</code></td></tr>
|
||||
@@ -5990,8 +5983,8 @@ flowchart LR
|
||||
<p><strong>关键规则</strong>:</p>
|
||||
<ul>
|
||||
<li><strong>一个端点 = 一个 Request 文件</strong>:Response DTO + Request 类放在同一文件中</li>
|
||||
<li><strong>Response DTO 必须有 <code>toEntity()</code></strong>:统一 DTO → Domain Entity 的转换入口</li>
|
||||
<li><strong>持久化 DTO 和 Response DTO 分开</strong>:Response DTO(<code>XxxData</code>)在 request 文件中,持久化 DTO(<code>XxxDto</code>)在 <code>data/models/</code></li>
|
||||
<li><strong>Response DTO 是纯 Dart 类</strong>:零注解、零 <code>factory fromJson</code>;只需字段 + 构造函数,<code>toEntity()</code> 按需添加</li>
|
||||
<li><strong>持久化 DTO 和 Response DTO 分开</strong>:Response DTO(<code>XxxResponse</code>)在 request 文件中,持久化 DTO(<code>XxxDto</code>)在 <code>data/models/</code></li>
|
||||
<li><strong>禁止跳层</strong>:ViewModel → Repository(→ UseCase 按需)→ NetworksSdkApi,每层职责明确</li>
|
||||
</ul>
|
||||
|
||||
@@ -6041,8 +6034,10 @@ part 'login_request.g.dart'; // 这行必须写!指向即将自动生成的
|
||||
<pre><code class="language-dart">// ── Response DTO ──
|
||||
|
||||
/// 服务端返回的登录数据
|
||||
@JsonSerializable() // ← 这个注解让 build_runner 自动生成 fromJson / toJson
|
||||
class LoginData {
|
||||
/// 纯 Dart 类,零注解,零样板。
|
||||
/// _$LoginResponseFromJson 由 ApiResponseGenerator 从 @ApiRequest(responseType: LoginResponse) 自动推导生成,
|
||||
/// 无需手动添加任何注解或 factory fromJson。
|
||||
class LoginResponse {
|
||||
final String token; // 服务端返回的字段
|
||||
@JsonKey(name: 'user_id') // 服务端字段名是 user_id,Dart 字段名是 userId
|
||||
final String userId;
|
||||
@@ -6050,18 +6045,15 @@ class LoginData {
|
||||
final String? nickname; // 可选字段用 String?
|
||||
final String? avatar;
|
||||
|
||||
const LoginData({ // 构造函数,参数和字段一一对应
|
||||
const LoginResponse({ // 构造函数,参数和字段一一对应
|
||||
required this.token,
|
||||
required this.userId,
|
||||
required this.email,
|
||||
this.nickname,
|
||||
this.avatar,
|
||||
});
|
||||
|
||||
// ↓ 这两行是固定写法,照抄就行,把类名替换掉
|
||||
factory LoginData.fromJson(Map<String, dynamic> json) =>
|
||||
_$LoginDataFromJson(json); // ← 短暂报红,watch 模式下保存后几秒自动消失
|
||||
Map<String, dynamic> toJson() => _$LoginDataToJson(this);
|
||||
// 不需要 factory fromJson,不需要 toJson,不需要任何注解
|
||||
// 生成器自动在 login_request.g.dart 中生成 _$LoginResponseFromJson 私有函数
|
||||
|
||||
/// DTO → Domain Entity(把网络层数据转为业务层数据)
|
||||
User toEntity() {
|
||||
@@ -6103,19 +6095,16 @@ class LoginData {
|
||||
@ApiRequest( // ← 这个注解让 build_runner 自动生成 path / method 等
|
||||
path: ApiPaths.authLogin, // 路径常量,定义在 core/foundation/api_paths.dart
|
||||
method: HttpMethod.post, // HTTP 方法,从接口文档抄
|
||||
responseType: LoginData, // 响应类型,就是上面写的 LoginData
|
||||
responseType: LoginResponse, // 响应类型,就是上面写的 LoginResponse
|
||||
requestType: ApiRequestType.login, // login 类型不携带 Token(登录前还没有 Token)
|
||||
)
|
||||
@JsonSerializable() // ← 自动生成 toJson(把请求参数序列化为 JSON)
|
||||
class LoginRequest extends ApiRequestable<LoginData> // ← 固定写法:extends ApiRequestable<响应类型>
|
||||
with _$LoginRequestApi { // ← 固定写法:with _$类名Api(短暂报红,保存后自动消失)
|
||||
class LoginRequest extends ApiRequestable<LoginResponse> // ← 固定写法:extends ApiRequestable<响应类型>
|
||||
with _$LoginRequestApi { // ← 固定写法:with _$类名Api(短暂报红,保存后自动消失)
|
||||
final String email; // 请求参数:要发给服务端的字段
|
||||
final String password;
|
||||
|
||||
LoginRequest({required this.email, required this.password});
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$LoginRequestToJson(this); // ← 固定写法,短暂报红,保存后自动消失
|
||||
// 完毕!toJson 由 _$LoginRequestApi mixin 从类字段自动生成,不需要 @JsonSerializable,不需要手写 toJson
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
@@ -6124,14 +6113,14 @@ class LoginRequest extends ApiRequestable<LoginData> // ← 固定写法
|
||||
<div style="background: #fff3cd; padding: 15px; border-radius: 8px; border-left: 4px solid #ffc107; margin: 15px 0;">
|
||||
<p style="margin-top: 0; font-weight: 700; color: #f57f17;">命名规则速查(写之前就能确定引用名)</p>
|
||||
<table>
|
||||
<thead><tr><th>你写的类名</th><th>fromJson</th><th>toJson</th><th>Api mixin</th></tr></thead>
|
||||
<thead><tr><th>你写的类名</th><th>fromJson(私有函数)</th><th>toJson</th><th>Api mixin</th><th>来源</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>LoginData</code></td><td><code>_$LoginDataFromJson</code></td><td><code>_$LoginDataToJson</code></td><td>-</td></tr>
|
||||
<tr><td><code>LoginRequest</code></td><td><code>_$LoginRequestFromJson</code></td><td><code>_$LoginRequestToJson</code></td><td><code>_$LoginRequestApi</code></td></tr>
|
||||
<tr><td><code>SendMessageRequest</code></td><td><code>_$SendMessageRequestFromJson</code></td><td><code>_$SendMessageRequestToJson</code></td><td><code>_$SendMessageRequestApi</code></td></tr>
|
||||
<tr><td><code>LoginResponse</code></td><td><code>_$LoginResponseFromJson</code></td><td>-(不需要)</td><td>-</td><td>ApiResponseGenerator 自动推导</td></tr>
|
||||
<tr><td><code>LoginRequest</code></td><td>-(不需要)</td><td>由 mixin 自动生成</td><td><code>_$LoginRequestApi</code></td><td>ApiRequestGenerator 生成 mixin</td></tr>
|
||||
<tr><td><code>SendMessageRequest</code></td><td>-(不需要)</td><td>由 mixin 自动生成</td><td><code>_$SendMessageRequestApi</code></td><td>ApiRequestGenerator 生成 mixin</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="margin-bottom: 0;">规则:<code>_$</code> + 类名 + <code>FromJson</code> / <code>ToJson</code> / <code>Api</code>。固定前缀,直接拼。</p>
|
||||
<p style="margin-bottom: 0;">规则:Response DTO 类名拼 <code>_$</code> + 类名 + <code>FromJson</code>(私有,只在 .g.dart 内部使用);Request 类名拼 <code>_$</code> + 类名 + <code>Api</code>(mixin)。</p>
|
||||
</div>
|
||||
|
||||
<!-- ────────── 第 2 步 ────────── -->
|
||||
@@ -6162,19 +6151,19 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
required String password,
|
||||
}) async {
|
||||
// 1. 调 NetworksSdkApi,构造请求 → 发 HTTP → 自动解码 → 返回 DTO
|
||||
final LoginData? loginData = await _client.executeRequest(
|
||||
final LoginResponse? loginResponse = await _client.executeRequest(
|
||||
LoginRequest(email: email, password: password),
|
||||
);
|
||||
|
||||
if (loginData == null) {
|
||||
if (loginResponse == null) {
|
||||
throw Exception('Login failed: empty response');
|
||||
}
|
||||
|
||||
// 2. 回调写入 Token(内存 + 持久化由 Provider 层组合)
|
||||
_onTokenUpdate(loginData.token);
|
||||
_onTokenUpdate(loginResponse.token);
|
||||
|
||||
// 3. DTO → Domain Entity,返回给上层
|
||||
return loginData.toEntity();
|
||||
return loginResponse.toEntity();
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
@@ -6321,10 +6310,10 @@ class LoginViewModel extends _$LoginViewModel {
|
||||
→ AuthRepositoryImpl.login() // data/repositories/
|
||||
→ _client.executeRequest(LoginRequest) // 调 NetworksSdkApi
|
||||
→ Auth → Encryption → Dio.request → Retry → Logging // 拦截器链自动处理
|
||||
← request.decodeResponse → LoginData.fromJson // 自动解码
|
||||
← LoginData(DTO)
|
||||
← request.decodeResponse → _$LoginResponseFromJson // 自动解码(生成的私有函数)
|
||||
← LoginResponse(DTO)
|
||||
→ onTokenUpdate(token) // 回调:内存写入 + 持久化
|
||||
← loginData.toEntity() → User(Domain Entity)
|
||||
← loginResponse.toEntity() → User(Domain Entity)
|
||||
← User
|
||||
← state = state.copyWith(user: user) // 更新状态
|
||||
← View: ref.watch → 自动 rebuild → UI 显示用户信息 // 自动刷新
|
||||
@@ -6345,22 +6334,18 @@ class LoginViewModel extends _$LoginViewModel {
|
||||
|
||||
<p><strong>你只需创建一个文件</strong>:<code>lib/data/remote/send_message_request.dart</code>,然后在 Repository 中调用即可。</p>
|
||||
|
||||
<pre><code class="language-dart">import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
<pre><code class="language-dart">import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
part 'send_message_request.g.dart';
|
||||
|
||||
// ── Response DTO ──
|
||||
// ── Response DTO(纯 Dart 类,零注解,_$SendMessageResponseFromJson 由生成器自动提供)──
|
||||
|
||||
@JsonSerializable()
|
||||
class SendMessageData {
|
||||
class SendMessageResponse {
|
||||
@JsonKey(name: 'message_id')
|
||||
final String messageId;
|
||||
final int timestamp;
|
||||
|
||||
const SendMessageData({required this.messageId, required this.timestamp});
|
||||
factory SendMessageData.fromJson(Map<String, dynamic> json) =>
|
||||
_$SendMessageDataFromJson(json);
|
||||
const SendMessageResponse({required this.messageId, required this.timestamp});
|
||||
}
|
||||
|
||||
// ── Request ──
|
||||
@@ -6368,26 +6353,24 @@ class SendMessageData {
|
||||
@ApiRequest(
|
||||
path: ApiPaths.chatSendMessage, // 路径常量,定义在 api_paths.dart
|
||||
method: HttpMethod.post, // HTTP 方法
|
||||
responseType: SendMessageData, // 响应类型
|
||||
responseType: SendMessageResponse, // 响应类型
|
||||
// requestType 不写,默认 ApiRequestType.request(会携带 Token)
|
||||
)
|
||||
@JsonSerializable()
|
||||
class SendMessageRequest extends ApiRequestable<SendMessageData>
|
||||
class SendMessageRequest extends ApiRequestable<SendMessageResponse>
|
||||
with _$SendMessageRequestApi {
|
||||
@JsonKey(name: 'chat_id') // JSON 字段名和 Dart 字段名不一样时用 @JsonKey
|
||||
final String chatId;
|
||||
final String content;
|
||||
|
||||
SendMessageRequest({required this.chatId, required this.content});
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$SendMessageRequestToJson(this);
|
||||
// toJson 自动生成:{'chat_id': chatId, 'content': content}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<p>保存 → 自动生成 → 然后在 Repository 中调 NetworksSdkApi 就完了:</p>
|
||||
|
||||
<pre><code class="language-dart">// 在 MessageRepositoryImpl 中添加
|
||||
Future<SendMessageData?> sendMessage({
|
||||
Future<SendMessageResponse?> sendMessage({
|
||||
required String chatId,
|
||||
required String content,
|
||||
}) {
|
||||
@@ -6407,22 +6390,19 @@ Future<SendMessageData?> sendMessage({
|
||||
|
||||
<pre><code class="language-dart">// lib/data/remote/get_profile_request.dart
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
part 'get_profile_request.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ProfileData {
|
||||
// ── Response DTO(纯 Dart 类,零注解,_$ProfileResponseFromJson 由生成器自动提供)──
|
||||
class ProfileResponse {
|
||||
@JsonKey(name: 'user_id')
|
||||
final String userId;
|
||||
final String email;
|
||||
final String? nickname;
|
||||
final String? avatar;
|
||||
|
||||
const ProfileData({required this.userId, required this.email, this.nickname, this.avatar});
|
||||
factory ProfileData.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProfileDataFromJson(json);
|
||||
const ProfileResponse({required this.userId, required this.email, this.nickname, this.avatar});
|
||||
|
||||
User toEntity() => User(id: userId, email: email, nickname: nickname, avatar: avatar);
|
||||
}
|
||||
@@ -6430,15 +6410,12 @@ class ProfileData {
|
||||
@ApiRequest(
|
||||
path: ApiPaths.userProfile,
|
||||
method: HttpMethod.get, // ← GET 请求,toJson() 结果作为 query string
|
||||
responseType: ProfileData,
|
||||
responseType: ProfileResponse,
|
||||
)
|
||||
@JsonSerializable()
|
||||
class GetProfileRequest extends ApiRequestable<ProfileData>
|
||||
class GetProfileRequest extends ApiRequestable<ProfileResponse>
|
||||
with _$GetProfileRequestApi {
|
||||
GetProfileRequest(); // 无参数 — token 标识当前用户,无需显式传 user_id
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$GetProfileRequestToJson(this);
|
||||
// toJson 自动生成空 map {}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
@@ -6685,41 +6662,33 @@ melos run gen:watch
|
||||
|
||||
<pre><code class="language-dart">// data/remote/login_request.dart
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
part 'login_request.g.dart'; // ← 必须写,指向即将生成的文件
|
||||
|
||||
// ── Response DTO ──
|
||||
@JsonSerializable()
|
||||
class LoginData {
|
||||
// ── Response DTO(纯 Dart 类,零注解,零样板)──
|
||||
// _$LoginResponseFromJson 由 ApiResponseGenerator 从 @ApiRequest(responseType: LoginResponse) 自动推导生成
|
||||
class LoginResponse {
|
||||
final String token;
|
||||
final String email;
|
||||
const LoginData({required this.token, required this.email});
|
||||
|
||||
// ↓ 此时 _$LoginDataFromJson 还不存在,IDE 会报红,正常!
|
||||
factory LoginData.fromJson(Map<String, dynamic> json) =>
|
||||
_$LoginDataFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$LoginDataToJson(this);
|
||||
const LoginResponse({required this.token, required this.email});
|
||||
// 不需要 factory fromJson,不需要任何注解
|
||||
}
|
||||
|
||||
// ── Request ──
|
||||
@ApiRequest(
|
||||
path: ApiPaths.authLogin,
|
||||
method: HttpMethod.post,
|
||||
responseType: LoginData,
|
||||
responseType: LoginResponse,
|
||||
requestType: ApiRequestType.login,
|
||||
)
|
||||
@JsonSerializable()
|
||||
class LoginRequest extends ApiRequestable<LoginData>
|
||||
class LoginRequest extends ApiRequestable<LoginResponse>
|
||||
with _$LoginRequestApi { // ← 短暂报红,保存后自动消失
|
||||
final String email;
|
||||
final String password;
|
||||
|
||||
LoginRequest({required this.email, required this.password});
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$LoginRequestToJson(this); // ← 短暂报红,保存后自动消失
|
||||
// toJson 由 mixin 自动生成,保存后红线自动消失
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
@@ -6734,11 +6703,10 @@ class LoginRequest extends ApiRequestable<LoginData>
|
||||
<p style="margin-top: 0; font-weight: 700; color: #c62828;">命名规则(写之前就能确定引用名)</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>注解</th><th>生成的符号</th><th>示例</th></tr></thead>
|
||||
<thead><tr><th>来源</th><th>生成的符号</th><th>示例</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>@JsonSerializable()</code></td><td><code>_$类名FromJson()</code></td><td><code>_$LoginDataFromJson(json)</code></td></tr>
|
||||
<tr><td><code>@JsonSerializable()</code></td><td><code>_$类名ToJson()</code></td><td><code>_$LoginDataToJson(this)</code></td></tr>
|
||||
<tr><td><code>@ApiRequest(...)</code></td><td><code>_$类名Api</code>(mixin)</td><td><code>_$LoginRequestApi</code></td></tr>
|
||||
<tr><td><code>@ApiRequest(responseType: T)</code><br/>(ApiResponseGenerator 推导)</td><td><code>_$类名FromJson</code>(私有函数,.g.dart 内部使用)</td><td><code>_$LoginResponseFromJson(json)</code></td></tr>
|
||||
<tr><td><code>@ApiRequest(...)</code> on Request 类<br/>(ApiRequestGenerator 生成 mixin)</td><td><code>_$类名Api</code>(mixin)</td><td><code>_$LoginRequestApi</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user