Merge branch 'dev' into happi/dev/database-update

# Conflicts:
#	apps/im_app/lib/data/models/user_dto.dart
#	apps/im_app/lib/data/remote/login_request.dart
#	apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart
#	apps/im_app/lib/features/chat/view/chat_db_test_page.dart
#	apps/im_app/lib/features/login/presentation/login_view_model.dart
This commit is contained in:
Happi (哈比)
2026-03-09 15:08:45 +08:00
163 changed files with 4341 additions and 1785 deletions

View File

@@ -6,28 +6,30 @@ import '../../../domain/entities/user.dart';
part 'get_profile_request.g.dart';
/// # /user/profile — 获取用户资料GET 请求示例
/// # /user/profile — 获取用户资料GET 请求)
///
/// 演示:GET 请求 + 无 body 参数的模式
/// GET 请求的 toJson() 结果会自动作为 URL query parameters 发送
/// GET 请求无 body`toJson()` 结果自动作为 URL query parameters 发送
/// 如需 query 参数(如分页),直接在类中添加字段,生成器自动序列化
///
/// ## 数据流位置
///
/// ```
/// UserRepositoryImpl.getProfile()
/// → _client.executeRequest( ★ GetProfileRequest ★ ) ← 你在这里
/// → _client.executeRequest( ★ GetProfileRequest ★ ) ← 你在这里
/// → 服务端 GET /user/profile
/// → 响应 JSON → ★ ProfileData ★ ← 也在这里
/// → ProfileData.toEntity() → User
/// → SDK 内部 ApiResponseWrapper 拆包 { code, message, data }
/// → ProfileResponse ★ = data 字段 ← 也在这里
/// → ProfileResponse.toEntity() → User
/// ```
// ─────────────────────────────────────────────
// Response DTO
// ─────────────────────────────────────────────
/// 用户资料响应 DTO只需反序列化禁止生成无用的 toJson
@JsonSerializable(createToJson: false)
class ProfileData {
/// 用户资料接口的业务响应数据(对应服务端 `data` 字段)。
///
/// `{ code, message }` 由 SDK 内部的 `ApiResponseWrapper` 统一处理。纯 Dart 类,无需任何注解。
class ProfileResponse {
final int uid;
final String uuid;
@JsonKey(name: 'last_online')
@@ -54,7 +56,7 @@ class ProfileData {
final int channelGroupId;
final String hint;
const ProfileData({
const ProfileResponse({
required this.uid,
required this.uuid,
required this.lastOnline,
@@ -74,10 +76,6 @@ class ProfileData {
required this.hint,
});
factory ProfileData.fromJson(Map<String, dynamic> json) =>
_$ProfileDataFromJson(json);
/// DTO → Domain Entity
User toEntity() => User(
uid: uid,
uuid: uuid,
@@ -102,23 +100,12 @@ class ProfileData {
// ─────────────────────────────────────────────
/// 获取用户资料请求GET无参数
///
/// GET 请求无 bodytoJson() 返回空 map。
/// 如需 query 参数(如分页),添加字段即可,
/// toJson() 会自动将字段序列化为 URL query string。
@ApiRequest(
path: ApiPaths.userProfile,
method: HttpMethod.get,
responseType: ProfileData,
responseType: ProfileResponse,
)
@JsonSerializable()
class GetProfileRequest extends ApiRequestable<ProfileData>
class GetProfileRequest extends ApiRequestable<ProfileResponse>
with _$GetProfileRequestApi {
GetProfileRequest();
factory GetProfileRequest.fromJson(Map<String, dynamic> json) =>
_$GetProfileRequestFromJson(json);
@override
Map<String, dynamic> toJson() => _$GetProfileRequestToJson(this);
}

View File

@@ -12,17 +12,20 @@ part 'login_request.g.dart';
///
/// ```
/// AuthRepositoryImpl.login(email, password)
/// → _client.executeRequest( ★ LoginRequest ★ ) ← 你在这里
/// → _client.executeRequest( ★ LoginRequest ★ ) ← 你在这里
/// → 服务端 POST /auth/login
/// → 响应 JSON → ★ LoginResponse ★ ← 也在这里
/// → LoginResponse.toEntity() → User
/// → SDK 内部 ApiResponseWrapper 拆包 { code, message, data }
/// → LoginResponse ★ = data 字段T in APIResponseWrapper<T> ← 也在这里
/// → LoginResponse.toEntity() → User
/// ```
// ─────────────────────────────────────────────
// Response DTO
// ─────────────────────────────────────────────
@JsonSerializable(createToJson: false)
/// 登录响应中的用户档案,嵌套在 [LoginResponse.profile] 中。
///
/// 纯 Dart 类,无需任何注解。`_$LoginProfileFromJson` 由生成器从 `@ApiRequest` 声明中自动推导生成。
class LoginProfile {
final int uid;
final String uuid;
@@ -70,9 +73,6 @@ class LoginProfile {
required this.hint,
});
factory LoginProfile.fromJson(Map<String, dynamic> json) =>
_$LoginProfileFromJson(json);
User toEntity() => User(
uid: uid,
uuid: uuid,
@@ -92,8 +92,11 @@ class LoginProfile {
);
}
@JsonSerializable(createToJson: false, explicitToJson: true)
class LoginData {
/// 登录接口的业务响应数据(对应服务端 `data` 字段,即 T in `APIResponseWrapper<T>`)。
///
/// `{ code, message }` 由 SDK 内部的 `ApiResponseWrapper` 统一处理,
/// App 层只接触此类,不感知 envelope 结构。纯 Dart 类,无需任何注解。
class LoginResponse {
@JsonKey(name: 'account_id')
final String accountId;
final LoginProfile profile;
@@ -107,7 +110,7 @@ class LoginData {
@JsonKey(name: 'login_data')
final String loginData;
const LoginData({
const LoginResponse({
required this.accountId,
required this.profile,
required this.nonce,
@@ -117,52 +120,28 @@ class LoginData {
required this.loginData,
});
factory LoginData.fromJson(Map<String, dynamic> json) =>
_$LoginDataFromJson(json);
User toEntity() => profile.toEntity();
}
/// Top-level envelope: { "code": 0, "message": "OK", "data": { ... } }
@JsonSerializable(createToJson: false, explicitToJson: true)
class LoginResponse {
final int code;
final String message;
final LoginData data;
const LoginResponse({
required this.code,
required this.message,
required this.data,
});
factory LoginResponse.fromJson(Map<String, dynamic> json) =>
_$LoginResponseFromJson(json);
User toEntity() => data.toEntity();
}
// ─────────────────────────────────────────────
// Request
// ─────────────────────────────────────────────
/// 登录请求
///
/// `@ApiRequest` 一个注解搞定一切:
/// - mixin 自动生成 path / method / requestType / includeToken / toJson
/// - parameters getter 自动注册 `_$LoginResponseFromJson` 到 SDK 全局注册表
@ApiRequest(
path: ApiPaths.authLogin,
method: HttpMethod.post,
responseType: LoginResponse,
requestType: ApiRequestType.login,
)
@JsonSerializable()
class LoginRequest extends ApiRequestable<LoginResponse>
with _$LoginRequestApi {
final String email;
final String password;
LoginRequest({required this.email, required this.password});
factory LoginRequest.fromJson(Map<String, dynamic> json) =>
_$LoginRequestFromJson(json);
@override
Map<String, dynamic> toJson() => _$LoginRequestToJson(this);
}

View File

@@ -2,14 +2,14 @@ import 'package:networks_sdk/networks_sdk.dart';
import '../../../core/foundation/api_paths.dart';
/// # /auth/logout — 登出接口(无响应数据示例)
part 'logout_request.g.dart';
/// # /auth/logout — 登出接口(无响应数据)
///
/// 演示POST 请求 + 无 Response DTO 的模式。
/// 服务端返回 `{"code": 0, "message": "ok"}` 无 data 字段,
/// `executeRequest` 返回 null调用方直接 await 即可。
///
/// 此接口不使用 @ApiRequest 注解,直接实现 ApiRequestable
/// 演示手动实现方式(适用于不需要代码生成器的简单接口)。
/// `responseType` 省略 → 生成器跳过 `fromJson` 注册mixin 泛型为 `void`。
///
/// ## 数据流位置
///
@@ -17,16 +17,9 @@ import '../../../core/foundation/api_paths.dart';
/// AuthRepositoryImpl.logout()
/// → _client.executeRequest( ★ LogoutRequest ★ ) ← 你在这里
/// → 服务端 POST /auth/logout
/// → 响应 {"code": 0, "message": "ok"} → null
/// → 响应 {"code": 0, "message": "ok"} → null(无 data
/// ```
class LogoutRequest extends ApiRequestable<void> {
@override
String get path => ApiPaths.authLogout;
@override
HttpMethod get method => HttpMethod.post;
/// 登出不需要请求体参数
@override
Map<String, dynamic> toJson() => {};
@ApiRequest(path: ApiPaths.authLogout, method: HttpMethod.post)
class LogoutRequest extends ApiRequestable<void> with _$LogoutRequestApi {
LogoutRequest();
}

View File

@@ -32,8 +32,7 @@ part 'upload_file_request.g.dart';
// Response DTO
// ─────────────────────────────────────────────
/// 文件上传响应 DTO只需反序列化禁止生成无用的 toJson
@JsonSerializable(createToJson: false)
/// 文件上传接口的业务响应数据(对应服务端 `data` 字段)。纯 Dart 类,无需任何注解。
class UploadResult {
final String url;
@@ -41,9 +40,6 @@ class UploadResult {
final String fileId;
const UploadResult({required this.url, required this.fileId});
factory UploadResult.fromJson(Map<String, dynamic> json) =>
_$UploadResultFromJson(json);
}
// ═════════════════════════════════════════════
@@ -52,7 +48,7 @@ class UploadResult {
/// FormData 上传请求
///
/// 上传到自有后端 `/upload/file`,响应为标准 `{ code, message, data }` 信封
/// 上传到自有后端 `/upload/file`,响应为标准 `{ code, message, data }` 格式
/// 无需 override `decodeResponse`。
@ApiRequest(
path: ApiPaths.uploadFile,
@@ -97,7 +93,7 @@ class S3UploadResponse {
/// - path 为完整的 presigned URLSDK 检测到 http 开头不拼 baseURL
/// - uploadData 为 Uint8List 二进制数据
/// - 自定义 headersContent-Type: application/octet-stream
/// - override decodeResponse — S3 返回 204 No Content 或 XML不是标准信封
/// - override decodeResponse — S3 返回 204 No Content 或 XML不是标准响应格式
class S3UploadRequest extends ApiRequestable<S3UploadResponse> {
final Uint8List data;
final String presignedURL;
@@ -115,8 +111,8 @@ class S3UploadRequest extends ApiRequestable<S3UploadResponse> {
@override
Map<String, String>? get customHeaders => {
'Content-Type': 'application/octet-stream',
};
'Content-Type': 'application/octet-stream',
};
@override
Map<String, dynamic> toJson() => {};
@@ -125,7 +121,7 @@ class S3UploadRequest extends ApiRequestable<S3UploadResponse> {
@override
Object? get uploadData => data;
/// S3 响应不走标准 { code, message, data } 信封,需要自定义解码
/// S3 响应不走标准 { code, message, data } 格式,需要自定义解码
///
/// 可能的响应:
/// - 204 No Content空 body→ 成功