From a066e9d2dc1cca4598e4834c7d02a766231ee31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Happi=20=28=E5=93=88=E6=AF=94=29?= Date: Sat, 7 Mar 2026 17:29:32 +0800 Subject: [PATCH] =?UTF-8?q?1=EF=BC=89=E4=BF=AE=E6=94=B9=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=91=BD=E5=90=8D=EF=BC=8C=E4=B8=8D=E5=92=8C=E4=B8=9A?= =?UTF-8?q?=E5=8A=A1=E4=BD=BF=E7=94=A8=E9=87=8D=E5=90=88=E3=80=82=202?= =?UTF-8?q?=EF=BC=89=E4=BF=AE=E6=94=B9user=20=E8=A1=A8=EF=BC=8Cuid?= =?UTF-8?q?=E4=B8=BAunique,=20=E8=81=8A=E5=A4=A9=E5=AE=A4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=B9name=20unique=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/im_app/assets/loginData.json | 31 +++ .../lib/data/local/drift/app_database.dart | 38 ++-- .../data/local/drift/tables/call_logs.dart | 2 +- .../data/local/drift/tables/chat_bots.dart | 2 +- .../local/drift/tables/chat_categories.dart | 4 +- .../lib/data/local/drift/tables/chats.dart | 2 +- .../drift/tables/discover_mini_apps.dart | 2 +- .../local/drift/tables/explore_mini_apps.dart | 2 +- .../drift/tables/favorite_mini_apps.dart | 2 +- .../local/drift/tables/favourite_details.dart | 2 +- .../data/local/drift/tables/favourites.dart | 2 +- .../lib/data/local/drift/tables/groups.dart | 2 +- .../lib/data/local/drift/tables/message.dart | 2 +- .../pending_friend_request_histories.dart | 2 +- .../local/drift/tables/recent_mini_apps.dart | 2 +- .../lib/data/local/drift/tables/retries.dart | 2 +- .../lib/data/local/drift/tables/sounds.dart | 2 +- .../lib/data/local/drift/tables/tags.dart | 2 +- .../drift/tables/user_request_histories.dart | 2 +- .../lib/data/local/drift/tables/users.dart | 5 +- .../data/local/drift/tables/workspaces.dart | 2 +- apps/im_app/lib/data/models/user_dto.dart | 182 +++++++++++++----- .../lib/data/remote/get_profile_request.dart | 74 +++++-- .../im_app/lib/data/remote/login_request.dart | 154 +++++++++++---- .../repositories/auth_repository_impl.dart | 16 +- apps/im_app/lib/domain/entities/user.dart | 80 +++++++- .../presentation/chat_db_test_view_model.dart | 5 +- .../login/presentation/login_view_model.dart | 24 ++- apps/im_app/pubspec.yaml | 2 + .../database_repository_impl.dart | 90 ++++----- 30 files changed, 531 insertions(+), 208 deletions(-) create mode 100644 apps/im_app/assets/loginData.json diff --git a/apps/im_app/assets/loginData.json b/apps/im_app/assets/loginData.json new file mode 100644 index 0000000..222e7c1 --- /dev/null +++ b/apps/im_app/assets/loginData.json @@ -0,0 +1,31 @@ +{ + "code": 0, + "message": "OK", + "data": { + "account_id": "1713925030yFMUBu", + "profile": { + "uid": 2137067, + "uuid": "1713925030yFMUBu", + "last_online": 1772819822, + "profile_pic": "Image/7e/f5/7ef5b60dd83a34a74c164a21fbd1f098/7ef5b60dd83a34a74c164a21fbd1f098.jpg", + "profile_pic_gaussian": "", + "nickname": "Happi(哈比)", + "contact": "86552205", + "country_code": "+65", + "email": "happi@winwayinfo.com", + "recovery_email": "", + "username": "happi", + "bio": "", + "relationship": 2, + "user_alias": null, + "channel_id": 1, + "channel_group_id": 1, + "hint": "1111" + }, + "nonce": "", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjIxMzcwNjcsInVkaWQiOjI2NjI2MzcsInNpZCI6IjE3NzI4NjU0NDc0Z1c2VWc9PSIsImlzcyI6ImFiYy5jb20iLCJhdWQiOlsidXNlciJdLCJleHAiOjE3NzQxNjE0NDcsIm5iZiI6MTc3Mjg2NTQ0NywiaWF0IjoxNzcyODY1NDQ3fQ.gUL6hyKgyPP8Tw9y7kRSq-ndNKfV9uGFhU4YKiQzg0I", + "refresh_token": "ps0FF3XayvnJB_P8Cnfu7w-uD781b1-vfmUjbrONZxI=", + "device_id": "SP1A.210812", + "login_data": "" + } +} diff --git a/apps/im_app/lib/data/local/drift/app_database.dart b/apps/im_app/lib/data/local/drift/app_database.dart index 638e0f7..adbcd58 100644 --- a/apps/im_app/lib/data/local/drift/app_database.dart +++ b/apps/im_app/lib/data/local/drift/app_database.dart @@ -29,25 +29,25 @@ class AppDatabase extends _$AppDatabase { return {}; } return { - Favourite: database.favourites, - Sound: database.sounds, - Tag: database.tags, - PendingFriendRequestHistory: database.pendingFriendRequestHistories, - Message: database.messages, - RecentMiniApp: database.recentMiniApps, - Retry: database.retries, - Group: database.groups, - FavoriteMiniApp: database.favoriteMiniApps, - DiscoverMiniApp: database.discoverMiniApps, - ChatCategory: database.chatCategories, - ChatBot: database.chatBots, - FavouriteDetail: database.favouriteDetails, - UserRequestHistory: database.userRequestHistories, - Workspace: database.workspaces, - User: database.users, - ExploreMiniApp: database.exploreMiniApps, - CallLog: database.callLogs, - Chat: database.chats, + DriftFavourite: database.favourites, + DriftSound: database.sounds, + DriftTag: database.tags, + DriftPendingFriendRequestHistory: database.pendingFriendRequestHistories, + DriftMessage: database.messages, + DriftRecentMiniApp: database.recentMiniApps, + DriftRetry: database.retries, + DriftGroup: database.groups, + DriftFavoriteMiniApp: database.favoriteMiniApps, + DriftDiscoverMiniApp: database.discoverMiniApps, + DriftChatCategory: database.chatCategories, + DriftChatBot: database.chatBots, + DriftFavouriteDetail: database.favouriteDetails, + DriftUserRequestHistory: database.userRequestHistories, + DriftWorkspace: database.workspaces, + DriftUser: database.users, + DriftExploreMiniApp: database.exploreMiniApps, + DriftCallLog: database.callLogs, + DriftChat: database.chats, }; } diff --git a/apps/im_app/lib/data/local/drift/tables/call_logs.dart b/apps/im_app/lib/data/local/drift/tables/call_logs.dart index c956bc8..5aae228 100644 --- a/apps/im_app/lib/data/local/drift/tables/call_logs.dart +++ b/apps/im_app/lib/data/local/drift/tables/call_logs.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('CallLog') +@DataClassName('DriftCallLog') class CallLogs extends Table { TextColumn get id => text()(); IntColumn get callerId => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/chat_bots.dart b/apps/im_app/lib/data/local/drift/tables/chat_bots.dart index eecffaa..c0837f4 100644 --- a/apps/im_app/lib/data/local/drift/tables/chat_bots.dart +++ b/apps/im_app/lib/data/local/drift/tables/chat_bots.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('ChatBot') +@DataClassName('DriftChatBot') class ChatBots extends Table { IntColumn get id => integer()(); TextColumn get name => text().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/chat_categories.dart b/apps/im_app/lib/data/local/drift/tables/chat_categories.dart index 2bd07d3..42b3f6c 100644 --- a/apps/im_app/lib/data/local/drift/tables/chat_categories.dart +++ b/apps/im_app/lib/data/local/drift/tables/chat_categories.dart @@ -1,9 +1,9 @@ import 'package:drift/drift.dart'; -@DataClassName('ChatCategory') +@DataClassName('DriftChatCategory') class ChatCategories extends Table { IntColumn get id => integer()(); - TextColumn get name => text().nullable().unique()(); + TextColumn get name => text().nullable()(); TextColumn get includedChatIds => text().nullable()(); TextColumn get excludedChatIds => text().nullable()(); IntColumn get seq => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/chats.dart b/apps/im_app/lib/data/local/drift/tables/chats.dart index 61eea86..eaff81a 100644 --- a/apps/im_app/lib/data/local/drift/tables/chats.dart +++ b/apps/im_app/lib/data/local/drift/tables/chats.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Chat') +@DataClassName('DriftChat') class Chats extends Table { IntColumn get id => integer()(); IntColumn get typ => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/discover_mini_apps.dart b/apps/im_app/lib/data/local/drift/tables/discover_mini_apps.dart index a3fbb06..44ba236 100644 --- a/apps/im_app/lib/data/local/drift/tables/discover_mini_apps.dart +++ b/apps/im_app/lib/data/local/drift/tables/discover_mini_apps.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('DiscoverMiniApp') +@DataClassName('DriftDiscoverMiniApp') class DiscoverMiniApps extends Table { TextColumn get id => text()(); TextColumn get name => text().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/explore_mini_apps.dart b/apps/im_app/lib/data/local/drift/tables/explore_mini_apps.dart index b4c2949..73d5750 100644 --- a/apps/im_app/lib/data/local/drift/tables/explore_mini_apps.dart +++ b/apps/im_app/lib/data/local/drift/tables/explore_mini_apps.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('ExploreMiniApp') +@DataClassName('DriftExploreMiniApp') class ExploreMiniApps extends Table { TextColumn get id => text()(); TextColumn get name => text().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/favorite_mini_apps.dart b/apps/im_app/lib/data/local/drift/tables/favorite_mini_apps.dart index 3f61047..9e38bb7 100644 --- a/apps/im_app/lib/data/local/drift/tables/favorite_mini_apps.dart +++ b/apps/im_app/lib/data/local/drift/tables/favorite_mini_apps.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('FavoriteMiniApp') +@DataClassName('DriftFavoriteMiniApp') class FavoriteMiniApps extends Table { TextColumn get id => text()(); TextColumn get name => text().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/favourite_details.dart b/apps/im_app/lib/data/local/drift/tables/favourite_details.dart index ea1ed95..678af16 100644 --- a/apps/im_app/lib/data/local/drift/tables/favourite_details.dart +++ b/apps/im_app/lib/data/local/drift/tables/favourite_details.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('FavouriteDetail') +@DataClassName('DriftFavouriteDetail') class FavouriteDetails extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get relatedId => text().withDefault(const Constant(''))(); diff --git a/apps/im_app/lib/data/local/drift/tables/favourites.dart b/apps/im_app/lib/data/local/drift/tables/favourites.dart index d2b2758..289e1d8 100644 --- a/apps/im_app/lib/data/local/drift/tables/favourites.dart +++ b/apps/im_app/lib/data/local/drift/tables/favourites.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Favourite') +@DataClassName('DriftFavourite') class Favourites extends Table { IntColumn get id => integer()(); TextColumn get parentId => text().withDefault(const Constant(''))(); diff --git a/apps/im_app/lib/data/local/drift/tables/groups.dart b/apps/im_app/lib/data/local/drift/tables/groups.dart index 228d491..61da3ba 100644 --- a/apps/im_app/lib/data/local/drift/tables/groups.dart +++ b/apps/im_app/lib/data/local/drift/tables/groups.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Group') +@DataClassName('DriftGroup') class Groups extends Table { IntColumn get id => integer()(); IntColumn get userJoinDate => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/message.dart b/apps/im_app/lib/data/local/drift/tables/message.dart index bd64691..30bb7c4 100644 --- a/apps/im_app/lib/data/local/drift/tables/message.dart +++ b/apps/im_app/lib/data/local/drift/tables/message.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Message') +@DataClassName('DriftMessage') class Messages extends Table { IntColumn get id => integer()(); IntColumn get messageId => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/pending_friend_request_histories.dart b/apps/im_app/lib/data/local/drift/tables/pending_friend_request_histories.dart index 05787b6..cddf2e6 100644 --- a/apps/im_app/lib/data/local/drift/tables/pending_friend_request_histories.dart +++ b/apps/im_app/lib/data/local/drift/tables/pending_friend_request_histories.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('PendingFriendRequestHistory') +@DataClassName('DriftPendingFriendRequestHistory') class PendingFriendRequestHistories extends Table { IntColumn get id => integer()(); IntColumn get uid => integer()(); diff --git a/apps/im_app/lib/data/local/drift/tables/recent_mini_apps.dart b/apps/im_app/lib/data/local/drift/tables/recent_mini_apps.dart index c149979..043b176 100644 --- a/apps/im_app/lib/data/local/drift/tables/recent_mini_apps.dart +++ b/apps/im_app/lib/data/local/drift/tables/recent_mini_apps.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('RecentMiniApp') +@DataClassName('DriftRecentMiniApp') class RecentMiniApps extends Table { TextColumn get id => text()(); TextColumn get name => text().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/retries.dart b/apps/im_app/lib/data/local/drift/tables/retries.dart index 19ad126..ca39eb6 100644 --- a/apps/im_app/lib/data/local/drift/tables/retries.dart +++ b/apps/im_app/lib/data/local/drift/tables/retries.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Retry') +@DataClassName('DriftRetry') class Retries extends Table { IntColumn get id => integer().autoIncrement()(); IntColumn get uid => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/sounds.dart b/apps/im_app/lib/data/local/drift/tables/sounds.dart index 84173f3..0e5eec1 100644 --- a/apps/im_app/lib/data/local/drift/tables/sounds.dart +++ b/apps/im_app/lib/data/local/drift/tables/sounds.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Sound') +@DataClassName('DriftSound') class Sounds extends Table { IntColumn get id => integer()(); TextColumn get filePath => text().withDefault(const Constant(''))(); diff --git a/apps/im_app/lib/data/local/drift/tables/tags.dart b/apps/im_app/lib/data/local/drift/tables/tags.dart index 15db57e..8da5fdc 100644 --- a/apps/im_app/lib/data/local/drift/tables/tags.dart +++ b/apps/im_app/lib/data/local/drift/tables/tags.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Tag') +@DataClassName('DriftTag') class Tags extends Table { IntColumn get id => integer().autoIncrement()(); IntColumn get uid => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/user_request_histories.dart b/apps/im_app/lib/data/local/drift/tables/user_request_histories.dart index d04e802..4ce2660 100644 --- a/apps/im_app/lib/data/local/drift/tables/user_request_histories.dart +++ b/apps/im_app/lib/data/local/drift/tables/user_request_histories.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('UserRequestHistory') +@DataClassName('DriftUserRequestHistory') class UserRequestHistories extends Table { IntColumn get id => integer()(); IntColumn get status => integer().nullable()(); diff --git a/apps/im_app/lib/data/local/drift/tables/users.dart b/apps/im_app/lib/data/local/drift/tables/users.dart index 57d48e5..b6f9cf9 100644 --- a/apps/im_app/lib/data/local/drift/tables/users.dart +++ b/apps/im_app/lib/data/local/drift/tables/users.dart @@ -1,9 +1,9 @@ import 'package:drift/drift.dart'; -@DataClassName('User') +@DataClassName('DriftUser') class Users extends Table { IntColumn get id => integer().autoIncrement()(); - IntColumn get uid => integer().nullable()(); + IntColumn get uid => integer().unique()(); TextColumn get uuid => text().nullable()(); IntColumn get lastOnline => integer().nullable()(); TextColumn get profilePic => text().nullable()(); @@ -36,6 +36,7 @@ class Users extends Table { TextColumn get publicKey => text().nullable()(); IntColumn get configBits => integer().withDefault(const Constant(0))(); TextColumn get hint => text().nullable()(); + @override String get tableName => 'user'; } \ No newline at end of file diff --git a/apps/im_app/lib/data/local/drift/tables/workspaces.dart b/apps/im_app/lib/data/local/drift/tables/workspaces.dart index 4b53b57..ea91f74 100644 --- a/apps/im_app/lib/data/local/drift/tables/workspaces.dart +++ b/apps/im_app/lib/data/local/drift/tables/workspaces.dart @@ -1,6 +1,6 @@ import 'package:drift/drift.dart'; -@DataClassName('Workspace') +@DataClassName('DriftWorkspace') class Workspaces extends Table { IntColumn get id => integer()(); TextColumn get name => text().nullable()(); diff --git a/apps/im_app/lib/data/models/user_dto.dart b/apps/im_app/lib/data/models/user_dto.dart index 5c05b30..beb55c0 100644 --- a/apps/im_app/lib/data/models/user_dto.dart +++ b/apps/im_app/lib/data/models/user_dto.dart @@ -1,68 +1,148 @@ -import 'package:json_annotation/json_annotation.dart'; - -import '../../domain/entities/user.dart'; - -part 'user_dto.g.dart'; +import 'package:drift/drift.dart'; +import 'package:im_app/data/local/drift/app_database.dart'; +import 'package:im_app/domain/entities/user.dart'; /// 用户 DTO(Data Transfer Object) /// /// local / remote 共用的数据传输对象,放在 data/models/。 /// 提供与 Domain Entity [User] 之间的双向转换。 -/// -/// ## 数据流位置(本地存储场景) -/// -/// ``` -/// 写入本地: -/// LoginData.toEntity() → User -/// → UserDto.fromEntity(user) → ★ UserDto ★ ← 你在这里 -/// → toJson() → SQLite / SharedPreferences -/// -/// 读取本地: -/// SQLite / SharedPreferences → JSON -/// → ★ UserDto.fromJson() ★ ← 你在这里 -/// → UserDto.toEntity() → User -/// → ViewModel.state → View -/// ``` -/// -/// 注意:登录接口的 Response DTO 是 [LoginData](含 token), -/// 本类用于纯用户信息的本地持久化,不含 token。 -@JsonSerializable() class UserDto { - @JsonKey(name: 'user_id') - final String userId; - final String email; + final int uid; + final String? uuid; + final int? lastOnline; + final String? profilePic; + final String? profilePicGaussian; final String? nickname; - final String? avatar; + final String? contact; + final String? countryCode; + final String? email; + final String? recoveryEmail; + final String? username; + final String? bio; + final int? relationship; + final String? userAlias; + final int? channelId; + final int? channelGroupId; + final String? hint; const UserDto({ - required this.userId, - required this.email, + required this.uid, + this.uuid, + this.lastOnline, + this.profilePic, + this.profilePicGaussian, this.nickname, - this.avatar, + this.contact, + this.countryCode, + this.email, + this.recoveryEmail, + this.username, + this.bio, + this.relationship, + this.userAlias, + this.channelId, + this.channelGroupId, + this.hint, }); - factory UserDto.fromJson(Map json) => - _$UserDtoFromJson(json); + factory UserDto.fromJson(Map json) => UserDto( + uid: json['uid'] as int, + uuid: json['uuid'], + lastOnline: json['last_online'], + profilePic: json['profile_pic'], + profilePicGaussian: json['profile_pic_gaussian'] ?? '', + nickname: json['nickname'], + contact: json['contact'], + countryCode: json['country_code'], + email: json['email'], + recoveryEmail: json['recovery_email'] ?? '', + username: json['username'], + bio: json['bio'] ?? '', + relationship: json['relationship'], + userAlias: json['user_alias'], + channelId: json['channel_id'], + channelGroupId: json['channel_group_id'], + hint: json['hint'], + ); - Map toJson() => _$UserDtoToJson(this); + Map toJson() => { + 'uid': uid, + 'uuid': uuid, + 'last_online': lastOnline, + 'profile_pic': profilePic, + 'profile_pic_gaussian': profilePicGaussian, + 'nickname': nickname, + 'contact': contact, + 'country_code': countryCode, + 'email': email, + 'recovery_email': recoveryEmail, + 'username': username, + 'bio': bio, + 'relationship': relationship, + 'user_alias': userAlias, + 'channel_id': channelId, + 'channel_group_id': channelGroupId, + 'hint': hint, + }; /// DTO → Domain Entity - User toEntity() { - return User( - id: userId, - email: email, - nickname: nickname, - avatar: avatar, - ); - } + User toEntity() => User( + uid: uid, + uuid: uuid, + lastOnline: lastOnline, + profilePic: profilePic, + profilePicGaussian: profilePicGaussian, + nickname: nickname, + contact: contact, + countryCode: countryCode, + email: email, + recoveryEmail: recoveryEmail, + username: username, + bio: bio, + relationship: relationship, + userAlias: userAlias, + channelId: channelId, + channelGroupId: channelGroupId, + hint: hint, + ); /// Domain Entity → DTO - factory UserDto.fromEntity(User user) { - return UserDto( - userId: user.id, - email: user.email, - nickname: user.nickname, - avatar: user.avatar, - ); - } -} + factory UserDto.fromEntity(User user) => UserDto( + uid: user.uid, + uuid: user.uuid, + lastOnline: user.lastOnline, + profilePic: user.profilePic, + profilePicGaussian: user.profilePicGaussian, + nickname: user.nickname, + contact: user.contact, + countryCode: user.countryCode, + email: user.email, + recoveryEmail: user.recoveryEmail, + username: user.username, + bio: user.bio, + relationship: user.relationship, + userAlias: user.userAlias, + channelId: user.channelId, + channelGroupId: user.channelGroupId, + hint: user.hint, + ); + + /// DTO → Drift Companion (for DB insert/update) + UsersCompanion toCompanion() => UsersCompanion( + uid: Value(uid), + uuid: Value(uuid), + lastOnline: Value(lastOnline), + profilePic: Value(profilePic), + profilePicGaussian: Value(profilePicGaussian ?? ''), + nickname: Value(nickname), + contact: Value(contact), + countryCode: Value(countryCode), + email: Value(email), + recoveryEmail: Value(recoveryEmail), + username: Value(username), + bio: Value(bio), + relationship: Value(relationship), + userAlias: Value(userAlias), + hint: Value(hint), + ); +} \ No newline at end of file diff --git a/apps/im_app/lib/data/remote/get_profile_request.dart b/apps/im_app/lib/data/remote/get_profile_request.dart index a02a3bb..5398026 100644 --- a/apps/im_app/lib/data/remote/get_profile_request.dart +++ b/apps/im_app/lib/data/remote/get_profile_request.dart @@ -28,31 +28,75 @@ part 'get_profile_request.g.dart'; /// 用户资料响应 DTO(只需反序列化,禁止生成无用的 toJson) @JsonSerializable(createToJson: false) class ProfileData { - @JsonKey(name: 'user_id') - final String userId; + final int uid; + final String uuid; + @JsonKey(name: 'last_online') + final int lastOnline; + @JsonKey(name: 'profile_pic') + final String profilePic; + @JsonKey(name: 'profile_pic_gaussian') + final String profilePicGaussian; + final String nickname; + final String contact; + @JsonKey(name: 'country_code') + final String countryCode; final String email; - final String? nickname; - final String? avatar; + @JsonKey(name: 'recovery_email') + final String recoveryEmail; + final String username; + final String bio; + final int relationship; + @JsonKey(name: 'user_alias') + final String? userAlias; + @JsonKey(name: 'channel_id') + final int channelId; + @JsonKey(name: 'channel_group_id') + final int channelGroupId; + final String hint; const ProfileData({ - required this.userId, + required this.uid, + required this.uuid, + required this.lastOnline, + required this.profilePic, + required this.profilePicGaussian, + required this.nickname, + required this.contact, + required this.countryCode, required this.email, - this.nickname, - this.avatar, + required this.recoveryEmail, + required this.username, + required this.bio, + required this.relationship, + this.userAlias, + required this.channelId, + required this.channelGroupId, + required this.hint, }); factory ProfileData.fromJson(Map json) => _$ProfileDataFromJson(json); /// DTO → Domain Entity - User toEntity() { - return User( - id: userId, - email: email, - nickname: nickname, - avatar: avatar, - ); - } + User toEntity() => User( + uid: uid, + uuid: uuid, + lastOnline: lastOnline, + profilePic: profilePic, + profilePicGaussian: profilePicGaussian, + nickname: nickname, + contact: contact, + countryCode: countryCode, + email: email, + recoveryEmail: recoveryEmail, + username: username, + bio: bio, + relationship: relationship, + userAlias: userAlias, + channelId: channelId, + channelGroupId: channelGroupId, + hint: hint, + ); } // ───────────────────────────────────────────── diff --git a/apps/im_app/lib/data/remote/login_request.dart b/apps/im_app/lib/data/remote/login_request.dart index e5e6468..4145d71 100644 --- a/apps/im_app/lib/data/remote/login_request.dart +++ b/apps/im_app/lib/data/remote/login_request.dart @@ -8,75 +8,155 @@ part 'login_request.g.dart'; /// # /auth/login — 登录接口 /// -/// 一个端点 = 一个文件,Response DTO + Request 放在同一文件中。 -/// /// ## 数据流位置 /// /// ``` /// AuthRepositoryImpl.login(email, password) /// → _client.executeRequest( ★ LoginRequest ★ ) ← 你在这里 /// → 服务端 POST /auth/login -/// → 响应 JSON → ★ LoginData ★ ← 也在这里 -/// → LoginData.toEntity() → User +/// → 响应 JSON → ★ LoginResponse ★ ← 也在这里 +/// → LoginResponse.toEntity() → User /// ``` // ───────────────────────────────────────────── // Response DTO // ───────────────────────────────────────────── -/// 登录响应 DTO -/// -/// 服务端返回的登录数据,包含 token 和用户信息。 -/// 通过 [toEntity] 转换为 Domain Entity [User]。 -@JsonSerializable() -class LoginData { - final String token; - @JsonKey(name: 'user_id') - final String userId; +@JsonSerializable(createToJson: false) +class LoginProfile { + final int uid; + final String uuid; + @JsonKey(name: 'last_online') + final int lastOnline; + @JsonKey(name: 'profile_pic') + final String profilePic; + @JsonKey(name: 'profile_pic_gaussian') + final String profilePicGaussian; + final String nickname; + final String contact; + @JsonKey(name: 'country_code') + final String countryCode; final String email; - final String? nickname; - final String? avatar; + @JsonKey(name: 'recovery_email') + final String recoveryEmail; + final String username; + final String bio; + final int relationship; + @JsonKey(name: 'user_alias') + final String? userAlias; + @JsonKey(name: 'channel_id') + final int channelId; + @JsonKey(name: 'channel_group_id') + final int channelGroupId; + final String hint; + + const LoginProfile({ + required this.uid, + required this.uuid, + required this.lastOnline, + required this.profilePic, + required this.profilePicGaussian, + required this.nickname, + required this.contact, + required this.countryCode, + required this.email, + required this.recoveryEmail, + required this.username, + required this.bio, + required this.relationship, + this.userAlias, + required this.channelId, + required this.channelGroupId, + required this.hint, + }); + + factory LoginProfile.fromJson(Map json) => + _$LoginProfileFromJson(json); + + User toEntity() => User( + uid: uid, + uuid: uuid, + lastOnline: lastOnline, + profilePic: profilePic, + profilePicGaussian: profilePicGaussian, + nickname: nickname, + contact: contact, + countryCode: countryCode, + email: email, + recoveryEmail: recoveryEmail, + username: username, + bio: bio, + relationship: relationship, + userAlias: userAlias, + channelId: channelId, + channelGroupId: channelGroupId, + hint: hint, + ); +} + +@JsonSerializable(createToJson: false, explicitToJson: true) +class LoginData { + @JsonKey(name: 'account_id') + final String accountId; + final LoginProfile profile; + final String nonce; + @JsonKey(name: 'access_token') + final String accessToken; + @JsonKey(name: 'refresh_token') + final String refreshToken; + @JsonKey(name: 'device_id') + final String deviceId; + @JsonKey(name: 'login_data') + final String loginData; const LoginData({ - required this.token, - required this.userId, - required this.email, - this.nickname, - this.avatar, + required this.accountId, + required this.profile, + required this.nonce, + required this.accessToken, + required this.refreshToken, + required this.deviceId, + required this.loginData, }); factory LoginData.fromJson(Map json) => _$LoginDataFromJson(json); - Map toJson() => _$LoginDataToJson(this); + User toEntity() => profile.toEntity(); +} - /// DTO → Domain Entity - User toEntity() { - return User( - id: userId, - email: email, - nickname: nickname, - avatar: avatar, - ); - } +/// 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 json) => + _$LoginResponseFromJson(json); + + User toEntity() => data.toEntity(); } // ───────────────────────────────────────────── // Request // ───────────────────────────────────────────── -/// 登录请求 -/// -/// `@ApiRequest` 自动生成 `_$LoginRequestApi` mixin, -/// 提供 path / method / requestType / includeToken / fromJson 自动注册。 @ApiRequest( path: ApiPaths.authLogin, method: HttpMethod.post, - responseType: LoginData, + responseType: LoginResponse, requestType: ApiRequestType.login, ) @JsonSerializable() -class LoginRequest extends ApiRequestable with _$LoginRequestApi { +class LoginRequest extends ApiRequestable + with _$LoginRequestApi { final String email; final String password; @@ -87,4 +167,4 @@ class LoginRequest extends ApiRequestable with _$LoginRequestApi { @override Map toJson() => _$LoginRequestToJson(this); -} +} \ No newline at end of file diff --git a/apps/im_app/lib/data/repositories/auth_repository_impl.dart b/apps/im_app/lib/data/repositories/auth_repository_impl.dart index 912e628..0dc93e2 100644 --- a/apps/im_app/lib/data/repositories/auth_repository_impl.dart +++ b/apps/im_app/lib/data/repositories/auth_repository_impl.dart @@ -30,18 +30,18 @@ class AuthRepositoryImpl implements AuthRepository { AuthRepositoryImpl({required NetworksSdkApi client, required void Function(String?) onTokenUpdate,}) : _client = client, _onTokenUpdate = onTokenUpdate; @override - Future login({required String email, required String password,}) async - { - final LoginData? loginData = await _client.executeRequest(LoginRequest(email: email, password: password),); + Future login({required String email, required String password}) async { + final LoginResponse? loginResponse = await _client.executeRequest( + LoginRequest(email: email, password: password), + ); - if (loginData == null) { - throw Exception('Login failed: empty response'); // TODO: 接入国际化 + if (loginResponse == null) { + throw Exception('Login failed: empty response'); } - // 回调写入 Token(内存 + 持久化由 Provider 层组合) - _onTokenUpdate(loginData.token); + _onTokenUpdate(loginResponse.data.accessToken); - return loginData.toEntity(); // DTO → Domain Entity + return loginResponse.toEntity(); } @override diff --git a/apps/im_app/lib/domain/entities/user.dart b/apps/im_app/lib/domain/entities/user.dart index 509d0ed..0e15597 100644 --- a/apps/im_app/lib/domain/entities/user.dart +++ b/apps/im_app/lib/domain/entities/user.dart @@ -14,15 +14,81 @@ /// → View 渲染 /// ``` class User { - final String id; - final String email; + final int uid; + final String? uuid; + final int? lastOnline; + final String? profilePic; + final String? profilePicGaussian; final String? nickname; - final String? avatar; + final String? contact; + final String? countryCode; + final String? email; + final String? recoveryEmail; + final String? username; + final String? bio; + final int? relationship; + final String? userAlias; + final int? channelId; + final int? channelGroupId; + final String? hint; const User({ - required this.id, - required this.email, + required this.uid, + this.uuid, + this.lastOnline, + this.profilePic, + this.profilePicGaussian, this.nickname, - this.avatar, + this.contact, + this.countryCode, + this.email, + this.recoveryEmail, + this.username, + this.bio, + this.relationship, + this.userAlias, + this.channelId, + this.channelGroupId, + this.hint, }); -} + + User copyWith({ + int? uid, + String? uuid, + int? lastOnline, + String? profilePic, + String? profilePicGaussian, + String? nickname, + String? contact, + String? countryCode, + String? email, + String? recoveryEmail, + String? username, + String? bio, + int? relationship, + String? userAlias, + int? channelId, + int? channelGroupId, + String? hint, + }) { + return User( + uid: uid ?? this.uid, + uuid: uuid ?? this.uuid, + lastOnline: lastOnline ?? this.lastOnline, + profilePic: profilePic ?? this.profilePic, + profilePicGaussian: profilePicGaussian ?? this.profilePicGaussian, + nickname: nickname ?? this.nickname, + contact: contact ?? this.contact, + countryCode: countryCode ?? this.countryCode, + email: email ?? this.email, + recoveryEmail: recoveryEmail ?? this.recoveryEmail, + username: username ?? this.username, + bio: bio ?? this.bio, + relationship: relationship ?? this.relationship, + userAlias: userAlias ?? this.userAlias, + channelId: channelId ?? this.channelId, + channelGroupId: channelGroupId ?? this.channelGroupId, + hint: hint ?? this.hint, + ); + } +} \ No newline at end of file diff --git a/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart b/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart index da6fa16..00df1da 100644 --- a/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart +++ b/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart @@ -1,7 +1,6 @@ import 'dart:math'; import 'package:drift/drift.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:im_app/app/di/db_provider.dart'; import 'package:im_app/data/local/drift/app_database.dart'; @@ -87,12 +86,12 @@ class ChatDbTestViewModel extends _$ChatDbTestViewModel { final chunk = List.generate( chunkSize.clamp(0, count - i), (j) => UsersCompanion.insert( - uid: Value(i + j), + uid: i + j, nickname: Value('User ${i + j}'), ), ); - await db.batchInsertOrReplace(chunk); + await db.batchInsertOrReplace(chunk); completed += chunk.length; // 让出主线程 diff --git a/apps/im_app/lib/features/login/presentation/login_view_model.dart b/apps/im_app/lib/features/login/presentation/login_view_model.dart index 0c29082..5045e40 100644 --- a/apps/im_app/lib/features/login/presentation/login_view_model.dart +++ b/apps/im_app/lib/features/login/presentation/login_view_model.dart @@ -1,4 +1,8 @@ -import 'package:flutter/foundation.dart'; +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:im_app/data/models/user_dto.dart'; +import 'package:im_app/data/remote/login_request.dart'; import 'package:networks_sdk/networks_sdk.dart'; import 'package:im_app/app/di/db_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -56,10 +60,22 @@ class LoginViewModel extends _$LoginViewModel { /// 正式 [login] 成功后同样需要调用 [AuthNotifier.login] 更新守卫状态。 Future demoLogin() async { final storageApi = ref.read(storageSdkProvider); - ///TODO: StorageSDKLifeCycle 需要只在主项目暴露 final storageLifeCycle = storageApi as StorageSdkLifecycle; - ref.read(authNotifierProvider).login(); - await storageLifeCycle.openDatabase(1234567); + final provider = ref.read(authNotifierProvider); + + // Read mock response from assets + final String raw = await rootBundle.loadString('assets/loginData.json'); + final Map json = jsonDecode(raw); + + // Parse into LoginData (nested under 'data' key) + final loginResponse = LoginResponse.fromJson(json); + final user = loginResponse.data.toEntity(); + + provider.login(); + // Open database for the user + await storageLifeCycle.openDatabase(user.uid); + final userCompanion = UserDto.fromEntity(user).toCompanion(); + storageApi.insert(userCompanion); } /// 执行登录 diff --git a/apps/im_app/pubspec.yaml b/apps/im_app/pubspec.yaml index e02f3fb..404e3a3 100644 --- a/apps/im_app/pubspec.yaml +++ b/apps/im_app/pubspec.yaml @@ -62,3 +62,5 @@ dev_dependencies: flutter: uses-material-design: true + assets: + - assets/ diff --git a/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart b/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart index a7f1246..1bb80f0 100644 --- a/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart +++ b/packages/storage_sdk/lib/src/data/repositories/database_repository_impl.dart @@ -18,19 +18,19 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future insertOrReplace( - TableInfo table, - Insertable companion, - ) async { + TableInfo table, + Insertable companion, + ) async { final db = _db; if (db == null) return; - await db.into(table).insertOnConflictUpdate(companion); + await db.into(table).insert(companion, mode: InsertMode.insertOrReplace); } @override Future insert( - TableInfo table, - Insertable companion, - ) async { + TableInfo table, + Insertable companion, + ) async { final db = _db; if (db == null) return; await db.into(table).insert(companion, mode: InsertMode.insertOrIgnore); @@ -38,13 +38,17 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future batchInsertOrReplace( - TableInfo table, - List> companions, - ) async { + TableInfo table, + List> companions, + ) async { final db = _db; if (db == null) return; await db.batch( - (batch) => batch.insertAllOnConflictUpdate(table, companions), + (batch) => batch.insertAll( + table, + companions, + mode: InsertMode.insertOrReplace, + ), ); } @@ -52,10 +56,10 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future updateWhere( - TableInfo table, - Insertable companion, - Expression Function(T) filter, - ) async { + TableInfo table, + Insertable companion, + Expression Function(T) filter, + ) async { final db = _db; if (db == null) return; await (db.update(table)..where(filter)).write(companion); @@ -65,9 +69,9 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future deleteWhere( - TableInfo table, - Expression Function(T) filter, - ) async { + TableInfo table, + Expression Function(T) filter, + ) async { final db = _db; if (db == null) return; await (db.delete(table)..where(filter)).go(); @@ -91,9 +95,9 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future> selectWhere( - TableInfo table, - Expression Function(T) filter, - ) async { + TableInfo table, + Expression Function(T) filter, + ) async { final db = _db; if (db == null) return []; return (db.select(table)..where(filter)).get(); @@ -101,9 +105,9 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future selectFirst( - TableInfo table, - Expression Function(T) filter, - ) async { + TableInfo table, + Expression Function(T) filter, + ) async { final db = _db; if (db == null) return null; return (db.select(table)..where(filter)..limit(1)).getSingleOrNull(); @@ -120,9 +124,9 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Stream> watchWhere( - TableInfo table, - Expression Function(T) filter, - ) { + TableInfo table, + Expression Function(T) filter, + ) { final db = _db; if (db == null) return const Stream.empty(); return (db.select(table)..where(filter)).watch(); @@ -130,9 +134,9 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Stream watchFirst( - TableInfo table, - Expression Function(T) filter, - ) { + TableInfo table, + Expression Function(T) filter, + ) { final db = _db; if (db == null) return const Stream.empty(); return (db.select(table)..where(filter)..limit(1)).watchSingleOrNull(); @@ -142,24 +146,24 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future> rawQuery( - String sql, [ - List args = const [], - ]) async { + String sql, [ + List args = const [], + ]) async { final db = _db; if (db == null) return []; return db .customSelect( - sql, - variables: args.map((e) => Variable(e)).toList(), - ) + sql, + variables: args.map((e) => Variable(e)).toList(), + ) .get(); } @override Future rawExecute( - String sql, [ - List args = const [], - ]) async { + String sql, [ + List args = const [], + ]) async { final db = _db; if (db == null) return; await db.customStatement(sql, args); @@ -169,9 +173,9 @@ class DatabaseRepositoryImpl implements DatabaseRepository { @override Future count( - TableInfo table, { - Expression Function(T)? filter, - }) async { + TableInfo table, { + Expression Function(T)? filter, + }) async { final db = _db; if (db == null) return 0; final countExpr = table.$columns.first.count(); @@ -180,4 +184,4 @@ class DatabaseRepositoryImpl implements DatabaseRepository { final row = await query.getSingle(); return row.read(countExpr) ?? 0; } -} +} \ No newline at end of file