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:
@@ -21,9 +21,30 @@ import 'package:im_app/data/local/drift/tables/chats.dart';
|
||||
|
||||
part 'app_database.g.dart';
|
||||
|
||||
@DriftDatabase(tables: [Favourites,Sounds,Tags,PendingFriendRequestHistories,Messages,RecentMiniApps,Retries,Groups,FavoriteMiniApps,DiscoverMiniApps,ChatCategories,ChatBots,FavouriteDetails,UserRequestHistories,Workspaces,Users,ExploreMiniApps,CallLogs,Chats]) //update mapping here
|
||||
@DriftDatabase(
|
||||
tables: [
|
||||
Favourites,
|
||||
Sounds,
|
||||
Tags,
|
||||
PendingFriendRequestHistories,
|
||||
Messages,
|
||||
RecentMiniApps,
|
||||
Retries,
|
||||
Groups,
|
||||
FavoriteMiniApps,
|
||||
DiscoverMiniApps,
|
||||
ChatCategories,
|
||||
ChatBots,
|
||||
FavouriteDetails,
|
||||
UserRequestHistories,
|
||||
Workspaces,
|
||||
Users,
|
||||
ExploreMiniApps,
|
||||
CallLogs,
|
||||
Chats,
|
||||
],
|
||||
) //update mapping here
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
|
||||
static Map<Type, TableInfo> getTableRegistry(GeneratedDatabase database) {
|
||||
if (database is! AppDatabase) {
|
||||
return {};
|
||||
@@ -67,7 +88,9 @@ class AppDatabase extends _$AppDatabase {
|
||||
// Create any new tables that don't exist yet
|
||||
for (final table in allTables) {
|
||||
final existingTables = await m.database
|
||||
.customSelect("SELECT name FROM sqlite_master WHERE type='table' AND name='${table.actualTableName}'")
|
||||
.customSelect(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='${table.actualTableName}'",
|
||||
)
|
||||
.get();
|
||||
|
||||
if (existingTables.isEmpty) {
|
||||
@@ -92,6 +115,4 @@ class AppDatabase extends _$AppDatabase {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ class CallLogs extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'call_log';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,4 @@ class ChatBots extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'chat_bot';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ class ChatCategories extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'chat_category';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ class Chats extends Table {
|
||||
IntColumn get outgoingIdx => integer().withDefault(const Constant(0))();
|
||||
IntColumn get incomingSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get outgoingSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get notificationSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get notificationSoundId =>
|
||||
integer().withDefault(const Constant(0))();
|
||||
TextColumn get chatKey => text().withDefault(const Constant(''))();
|
||||
TextColumn get activeChatKey => text().withDefault(const Constant(''))();
|
||||
IntColumn get coverIdx => integer().withDefault(const Constant(0))();
|
||||
@@ -55,4 +56,4 @@ class Chats extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'chat';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,4 @@ class DiscoverMiniApps extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'discover_mini_app';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,4 @@ class ExploreMiniApps extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'explore_mini_app';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,4 @@ class FavoriteMiniApps extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'favorite_mini_app';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ class FavouriteDetails extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'favourite_detail';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,4 @@ class Favourites extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'favourite';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,4 @@ class Groups extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'chat_group';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,4 @@ class Messages extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'message';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@ class PendingFriendRequestHistories extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'pending_friend_request_histories';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,4 @@ class RecentMiniApps extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'recent_mini_app';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ class Retries extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'retry';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ class Sounds extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'sound';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ class Tags extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'tags';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ class UserRequestHistories extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'user_request_history';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,12 @@ class Users extends Table {
|
||||
IntColumn get addIndex => integer().nullable()();
|
||||
IntColumn get incomingSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get outgoingSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get notificationSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get sendMessageSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get groupNotificationSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get notificationSoundId =>
|
||||
integer().withDefault(const Constant(0))();
|
||||
IntColumn get sendMessageSoundId =>
|
||||
integer().withDefault(const Constant(0))();
|
||||
IntColumn get groupNotificationSoundId =>
|
||||
integer().withDefault(const Constant(0))();
|
||||
TextColumn get groupTags => text().withDefault(const Constant('[]'))();
|
||||
TextColumn get friendTags => text().withDefault(const Constant('[]'))();
|
||||
TextColumn get publicKey => text().nullable()();
|
||||
@@ -39,4 +42,4 @@ class Users extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'user';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ class Workspaces extends Table {
|
||||
|
||||
@override
|
||||
String get tableName => 'workspace';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ class CallLogDto {
|
||||
deletedAt: Value(deletedAt),
|
||||
isRead: Value(isRead),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,4 +176,4 @@ class ChatBotDto {
|
||||
isAllowForward: Value(isAllowForward),
|
||||
tips: Value(tips),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +87,4 @@ class ChatCategoryDto {
|
||||
updatedAt: Value(updatedAt),
|
||||
deletedAt: Value(deletedAt),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,4 +197,4 @@ class Chat {
|
||||
localPermission: localPermission ?? this.localPermission,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,4 +109,4 @@ class DiscoverMiniApp {
|
||||
screen: screen ?? this.screen,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,34 +143,33 @@ class ExploreMiniAppDto {
|
||||
screen: screen,
|
||||
);
|
||||
|
||||
factory ExploreMiniAppDto.fromEntity(ExploreMiniApp app) =>
|
||||
ExploreMiniAppDto(
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
openuid: app.openuid,
|
||||
devId: app.devId,
|
||||
icon: app.icon,
|
||||
iconGaussian: app.iconGaussian,
|
||||
downloadUrl: app.downloadUrl,
|
||||
description: app.description,
|
||||
version: app.version,
|
||||
typ: app.typ,
|
||||
flag: app.flag,
|
||||
reviewStatus: app.reviewStatus,
|
||||
favoriteAt: app.favoriteAt,
|
||||
isActive: app.isActive,
|
||||
createdAt: app.createdAt,
|
||||
updatedAt: app.updatedAt,
|
||||
deletedAt: app.deletedAt,
|
||||
score: app.score,
|
||||
channels: app.channels,
|
||||
devName: app.devName,
|
||||
pictureGaussian: app.pictureGaussian,
|
||||
picture: app.picture,
|
||||
commentNum: app.commentNum,
|
||||
lastLoginAt: app.lastLoginAt,
|
||||
screen: app.screen,
|
||||
);
|
||||
factory ExploreMiniAppDto.fromEntity(ExploreMiniApp app) => ExploreMiniAppDto(
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
openuid: app.openuid,
|
||||
devId: app.devId,
|
||||
icon: app.icon,
|
||||
iconGaussian: app.iconGaussian,
|
||||
downloadUrl: app.downloadUrl,
|
||||
description: app.description,
|
||||
version: app.version,
|
||||
typ: app.typ,
|
||||
flag: app.flag,
|
||||
reviewStatus: app.reviewStatus,
|
||||
favoriteAt: app.favoriteAt,
|
||||
isActive: app.isActive,
|
||||
createdAt: app.createdAt,
|
||||
updatedAt: app.updatedAt,
|
||||
deletedAt: app.deletedAt,
|
||||
score: app.score,
|
||||
channels: app.channels,
|
||||
devName: app.devName,
|
||||
pictureGaussian: app.pictureGaussian,
|
||||
picture: app.picture,
|
||||
commentNum: app.commentNum,
|
||||
lastLoginAt: app.lastLoginAt,
|
||||
screen: app.screen,
|
||||
);
|
||||
|
||||
ExploreMiniAppsCompanion toCompanion() => ExploreMiniAppsCompanion(
|
||||
id: Value(id),
|
||||
@@ -199,4 +198,4 @@ class ExploreMiniAppDto {
|
||||
lastLoginAt: Value(lastLoginAt),
|
||||
screen: Value(screen),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,4 +199,4 @@ class FavoriteMiniAppDto {
|
||||
lastLoginAt: Value(lastLoginAt),
|
||||
screen: Value(screen),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +80,4 @@ class FavouriteDetailDto {
|
||||
chatId: Value(chatId),
|
||||
sendTime: Value(sendTime),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,4 +127,4 @@ class FavouriteDto {
|
||||
isUploaded: Value(isUploaded),
|
||||
urls: Value(urls),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,4 +218,4 @@ class GroupDto {
|
||||
topic: Value(topic),
|
||||
rp: Value(rp),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,4 +134,4 @@ class MessageDto {
|
||||
flag: Value(flag),
|
||||
cmid: Value(cmid),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,33 +31,33 @@ class PendingFriendRequestHistoryDto {
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'uid': uid,
|
||||
'request_time': requestTime,
|
||||
'remarks': remarks,
|
||||
'source': source,
|
||||
'rs': rs,
|
||||
};
|
||||
'id': id,
|
||||
'uid': uid,
|
||||
'request_time': requestTime,
|
||||
'remarks': remarks,
|
||||
'source': source,
|
||||
'rs': rs,
|
||||
};
|
||||
|
||||
PendingFriendRequestHistory toEntity() => PendingFriendRequestHistory(
|
||||
id: id,
|
||||
uid: uid,
|
||||
requestTime: requestTime,
|
||||
remarks: remarks,
|
||||
source: source,
|
||||
rs: rs,
|
||||
);
|
||||
id: id,
|
||||
uid: uid,
|
||||
requestTime: requestTime,
|
||||
remarks: remarks,
|
||||
source: source,
|
||||
rs: rs,
|
||||
);
|
||||
|
||||
factory PendingFriendRequestHistoryDto.fromEntity(
|
||||
PendingFriendRequestHistory history) =>
|
||||
PendingFriendRequestHistoryDto(
|
||||
id: history.id,
|
||||
uid: history.uid,
|
||||
requestTime: history.requestTime,
|
||||
remarks: history.remarks,
|
||||
source: history.source,
|
||||
rs: history.rs,
|
||||
);
|
||||
PendingFriendRequestHistory history,
|
||||
) => PendingFriendRequestHistoryDto(
|
||||
id: history.id,
|
||||
uid: history.uid,
|
||||
requestTime: history.requestTime,
|
||||
remarks: history.remarks,
|
||||
source: history.source,
|
||||
rs: history.rs,
|
||||
);
|
||||
|
||||
PendingFriendRequestHistoriesCompanion toCompanion() =>
|
||||
PendingFriendRequestHistoriesCompanion(
|
||||
@@ -68,4 +68,4 @@ class PendingFriendRequestHistoryDto {
|
||||
source: Value(source),
|
||||
rs: Value(rs),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,4 +198,4 @@ class RecentMiniAppDto {
|
||||
lastLoginAt: Value(lastLoginAt),
|
||||
screen: Value(screen),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,4 +106,4 @@ class RetryDto {
|
||||
createTime: Value(createTime),
|
||||
addIndex: Value(addIndex),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,4 +85,4 @@ class SoundDto {
|
||||
channelGroupId: Value(channelGroupId),
|
||||
isDefault: Value(isDefault),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +71,4 @@ class TagDto {
|
||||
updatedAt: Value(updatedAt),
|
||||
addIndex: Value(addIndex),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@ class UserRequestHistoryDto {
|
||||
final int? status;
|
||||
final int? createdAt;
|
||||
|
||||
const UserRequestHistoryDto({
|
||||
required this.id,
|
||||
this.status,
|
||||
this.createdAt,
|
||||
});
|
||||
const UserRequestHistoryDto({required this.id, this.status, this.createdAt});
|
||||
|
||||
factory UserRequestHistoryDto.fromJson(Map<String, dynamic> json) =>
|
||||
UserRequestHistoryDto(
|
||||
@@ -27,11 +23,8 @@ class UserRequestHistoryDto {
|
||||
'created_at': createdAt,
|
||||
};
|
||||
|
||||
UserRequestHistory toEntity() => UserRequestHistory(
|
||||
id: id,
|
||||
status: status,
|
||||
createdAt: createdAt,
|
||||
);
|
||||
UserRequestHistory toEntity() =>
|
||||
UserRequestHistory(id: id, status: status, createdAt: createdAt);
|
||||
|
||||
factory UserRequestHistoryDto.fromEntity(UserRequestHistory history) =>
|
||||
UserRequestHistoryDto(
|
||||
@@ -40,10 +33,9 @@ class UserRequestHistoryDto {
|
||||
createdAt: history.createdAt,
|
||||
);
|
||||
|
||||
UserRequestHistoriesCompanion toCompanion() =>
|
||||
UserRequestHistoriesCompanion(
|
||||
id: Value(id),
|
||||
status: Value(status),
|
||||
createdAt: Value(createdAt),
|
||||
);
|
||||
}
|
||||
UserRequestHistoriesCompanion toCompanion() => UserRequestHistoriesCompanion(
|
||||
id: Value(id),
|
||||
status: Value(status),
|
||||
createdAt: Value(createdAt),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,4 +113,4 @@ class WorkspaceDto {
|
||||
deletedAt: Value(deletedAt),
|
||||
channelGroupId: Value(channelGroupId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 请求无 body,toJson() 返回空 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 URL(SDK 检测到 http 开头不拼 baseURL)
|
||||
/// - uploadData 为 Uint8List 二进制数据
|
||||
/// - 自定义 headers(Content-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)→ 成功
|
||||
|
||||
@@ -8,7 +8,7 @@ import '../remote/logout_request.dart';
|
||||
/// 认证 Repository 实现
|
||||
///
|
||||
/// implements [AuthRepository] 接口(domain/repositories/ 中定义)。
|
||||
/// 直接使用 [ApiClient] 发送请求,将 DTO 转为 Domain Entity。
|
||||
/// 直接使用 [NetworksSdkApi] 发送请求,将 DTO 转为 Domain Entity。
|
||||
/// 后续可加 Local DataSource 实现离线缓存。
|
||||
///
|
||||
/// ## 数据流位置
|
||||
@@ -16,18 +16,22 @@ import '../remote/logout_request.dart';
|
||||
/// ```
|
||||
/// LoginUseCase.execute(email, password)
|
||||
/// → ★ AuthRepositoryImpl.login() ★ ← 你在这里
|
||||
/// → ApiClient.executeRequest(LoginRequest)
|
||||
/// → NetworksSdkApi.executeRequest(LoginRequest)
|
||||
/// → 服务端 POST /auth/login
|
||||
/// ← LoginData(Response DTO)
|
||||
/// → onTokenUpdate(token) ← 回调写入 Token
|
||||
/// ← LoginData.toEntity() → User ← DTO → Entity 转换在这里
|
||||
/// ← LoginResponse(SDK 已拆包 { code, message, data } envelope)
|
||||
/// → _onTokenUpdate(accessToken) ← 回调写入 Token
|
||||
/// ← LoginResponse.toEntity() → User ← DTO → Entity 转换在这里
|
||||
/// ← User(Domain Entity)
|
||||
/// ```
|
||||
class AuthRepositoryImpl implements AuthRepository {
|
||||
final NetworksSdkApi _client;
|
||||
final void Function(String?) _onTokenUpdate;
|
||||
|
||||
AuthRepositoryImpl({required NetworksSdkApi client, required void Function(String?) onTokenUpdate,}) : _client = client, _onTokenUpdate = onTokenUpdate;
|
||||
AuthRepositoryImpl({
|
||||
required NetworksSdkApi client,
|
||||
required void Function(String?) onTokenUpdate,
|
||||
}) : _client = client,
|
||||
_onTokenUpdate = onTokenUpdate;
|
||||
|
||||
@override
|
||||
Future<User> login({required String email, required String password}) async {
|
||||
@@ -39,7 +43,7 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
throw Exception('Login failed: empty response');
|
||||
}
|
||||
|
||||
_onTokenUpdate(loginResponse.data.accessToken);
|
||||
_onTokenUpdate(loginResponse.accessToken);
|
||||
|
||||
return loginResponse.toEntity();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user