- 移除 @riverpod/@freezed 注解依赖,全部改为手写 Provider(无需 build_runner) · LoginState 改为纯 Dart,LoginViewModel/ThemeViewModel/ChatViewModel 改为 Notifier · UserNotifier 改为 FamilyAsyncNotifier<User?,int>,mini_app_provider 改为手写 Provider · 15 个 StreamProvider/StreamProvider.family 从 @riverpod 迁移至手写 - 发送消息(#25) · SendMessageRequest/SendMessageResponse DTO · SendMessageUseCase:乐观写入 DB → HTTP POST → 更新 Chat 摘要 - 接收消息 WS(#26) · WsMessageService:监听 mode2 WS 帧 → HTTP 补拉 → DB 写入 → Chat 更新 · FetchHistoryRequest/FetchHistoryResponse DTO(GET /app/api/chat/history) · FetchHistoryUseCase:拉取 → insertOrReplaceAll - DI 装配(chat_service_providers.dart) · wsMessageServiceProvider、sendMessageUseCaseProvider、fetchHistoryUseCaseProvider - 聊天列表页(#27) · ChatListViewModel(Notifier<void>)+ chat_page.dart 真实会话列表 UI · ListTile:头像首字母、最新消息摘要、未读角标、时间格式化 - 聊天详情页(#28) · ChatDetailViewModel(FamilyNotifier<ChatDetailState,int>)+ chat_detail_page.dart · 消息气泡(自己/他人分左右)、底部输入框、发送状态与错误提示 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
132 lines
4.5 KiB
Dart
132 lines
4.5 KiB
Dart
import 'package:json_annotation/json_annotation.dart';
|
||
import 'package:networks_sdk/networks_sdk.dart';
|
||
|
||
import 'package:im_app/core/foundation/api_paths.dart';
|
||
import 'package:im_app/domain/entities/user.dart';
|
||
|
||
part 'get_profile_request.g.dart';
|
||
|
||
/// # /user/profile — 获取用户资料(GET 请求)
|
||
///
|
||
/// GET 请求无 body,`toJson()` 结果自动作为 URL query parameters 发送。
|
||
/// 如需 query 参数(如分页),直接在类中添加字段,生成器自动序列化。
|
||
///
|
||
/// ## 数据流位置
|
||
///
|
||
/// ```
|
||
/// UserRepositoryImpl.getProfile()
|
||
/// → _client.executeRequest( ★ GetProfileRequest ★ ) ← 你在这里
|
||
/// → 服务端 GET /user/profile
|
||
/// → SDK 内部 ApiResponseWrapper 拆包 { code, message, data }
|
||
/// → ★ ProfileResponse ★ = data 字段 ← 也在这里
|
||
/// → ProfileResponse.toEntity() → User
|
||
/// ```
|
||
|
||
// ─────────────────────────────────────────────
|
||
// Response DTO
|
||
// ─────────────────────────────────────────────
|
||
|
||
/// 用户资料接口的业务响应数据(对应服务端 `data` 字段)。
|
||
///
|
||
/// `{ code, message }` 由 SDK 内部的 `ApiResponseWrapper` 统一处理。纯 Dart 类,无需任何注解。
|
||
class ProfileResponse {
|
||
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;
|
||
@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 ProfileResponse({
|
||
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 ProfileResponse.fromJson(Map<String, dynamic> json) => ProfileResponse(
|
||
uid: (json['uid'] as num?)?.toInt() ?? 0,
|
||
uuid: json['uuid'] as String? ?? '',
|
||
lastOnline: (json['last_online'] as num?)?.toInt() ?? 0,
|
||
profilePic: json['profile_pic'] as String? ?? '',
|
||
profilePicGaussian: json['profile_pic_gaussian'] as String? ?? '',
|
||
nickname: json['nickname'] as String? ?? '',
|
||
contact: json['contact'] as String? ?? '',
|
||
countryCode: json['country_code'] as String? ?? '',
|
||
email: json['email'] as String? ?? '',
|
||
recoveryEmail: json['recovery_email'] as String? ?? '',
|
||
username: json['username'] as String? ?? '',
|
||
bio: json['bio'] as String? ?? '',
|
||
relationship: (json['relationship'] as num?)?.toInt() ?? 0,
|
||
userAlias: json['user_alias'] as String?,
|
||
channelId: (json['channel_id'] as num?)?.toInt() ?? 0,
|
||
channelGroupId: (json['channel_group_id'] as num?)?.toInt() ?? 0,
|
||
hint: json['hint'] as String? ?? '',
|
||
);
|
||
|
||
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,
|
||
hint: hint,
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────
|
||
// Request
|
||
// ─────────────────────────────────────────────
|
||
|
||
/// 获取用户资料请求(GET,无参数)
|
||
@ApiRequest(
|
||
path: ApiPaths.userProfile,
|
||
method: HttpMethod.get,
|
||
responseType: ProfileResponse,
|
||
)
|
||
class GetProfileRequest extends ApiRequestable<ProfileResponse>
|
||
with _$GetProfileRequestApi {
|
||
GetProfileRequest();
|
||
}
|