Files
customer-im-client-dev/apps/im_app/lib/data/remote/call_log_request.dart
pp-bot 8744e2c0b7 feat(settings): 收藏列表 + 最近呼叫全量实现(#42~#45)
## 收藏(Gitea #42~#45)
- `FetchFavoritesRequest` / `DeleteFavoriteRequest`:ApiRequestable,对齐 iOS FavouriteService
- `FetchFavoritesUseCase`:GET 分页拉取 → upsert FavoriteRepository
- `DeleteFavoriteUseCase`:POST delete → 同步删本地 DB
- `FavoritesViewModel`:分页/刷新/加载更多/删除,DB Stream 驱动
- `FavoritesPage`:列表 + RefreshIndicator + Dismissible 左滑删除 + 类型图标 + 空状态
- `AppRouteName.settingsFavorites` + 路由注册 + auth guard
- `settings_page.dart` 收藏行 onTap 接入导航

## 最近呼叫(框架,API 对接待续)
- `CallLogRequest` / `FetchCallLogsUseCase` / `RecentCallsViewModel`
- `RecentCallsPage`:双 Tab(全部/未接)+ _CallLogTile(图标/时长/时间)
- `AppRouteName.settingsRecentCalls` + 路由注册

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:30:56 +09:00

143 lines
4.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:networks_sdk/networks_sdk.dart';
import 'package:im_app/core/foundation/api_paths.dart';
import 'package:im_app/domain/entities/call_log.dart';
// ── 通话记录 DTO ──────────────────────────────────────────────────────────────
/// 服务端单条通话记录 DTO
///
/// 字段对齐 `im-client-im-dev` Call.fromJson
/// - `rtc_channel_id` → [id]主键String
/// - `inviter_id` / `caller_id` → [callerId]
/// - `video_call` → [videoCall]0=语音, 1=视频)
/// - `status` = 3/4/5/6 → 未接来电(配合客户端侧判断)
class CallLogDto {
final String id;
final int? callerId;
final int? receiverId;
final int? chatId;
final int? duration;
final int? videoCall;
final int? createdAt;
final int? updatedAt;
final int? endedAt;
final int? status;
final int? isDeleted;
final int? deletedAt;
final int? isRead;
const CallLogDto({
required this.id,
this.callerId,
this.receiverId,
this.chatId,
this.duration,
this.videoCall,
this.createdAt,
this.updatedAt,
this.endedAt,
this.status,
this.isDeleted,
this.deletedAt,
this.isRead,
});
factory CallLogDto.fromJson(Map<String, dynamic> json) => CallLogDto(
id: (json['rtc_channel_id'] ?? json['channel_id'] ?? json['id'] ?? '')
.toString(),
callerId:
(json['inviter_id'] ?? json['caller_id'] as num?)?.toInt(),
receiverId: (json['receiver_id'] as num?)?.toInt(),
chatId: (json['chat_id'] as num?)?.toInt(),
duration: (json['duration'] as num?)?.toInt(),
videoCall: (json['video_call'] as num?)?.toInt(),
createdAt: (json['created_at'] as num?)?.toInt(),
updatedAt: (json['updated_at'] as num?)?.toInt(),
endedAt: (json['ended_at'] as num?)?.toInt(),
status: (json['status'] as num?)?.toInt(),
isDeleted: (json['is_deleted'] as num?)?.toInt(),
deletedAt: (json['deleted_at'] as num?)?.toInt(),
isRead: (json['is_read'] as num?)?.toInt(),
);
CallLog toEntity() => CallLog(
id: id,
callerId: callerId,
receiverId: receiverId,
chatId: chatId,
duration: duration,
videoCall: videoCall,
createdAt: createdAt,
updatedAt: updatedAt,
endedAt: endedAt,
status: status,
isDeleted: isDeleted,
deletedAt: deletedAt,
isRead: isRead,
);
}
// ── FetchCallLogsResponse ────────────────────────────────────────────────────
class FetchCallLogsResponse {
final List<CallLogDto> records;
const FetchCallLogsResponse({required this.records});
factory FetchCallLogsResponse.fromJson(Map<String, dynamic> json) {
final list = json['list'] ?? json['records'] ?? json['data'] ?? [];
return FetchCallLogsResponse(
records: (list as List)
.map((item) => CallLogDto.fromJson(item as Map<String, dynamic>))
.toList(),
);
}
}
// ── FetchCallLogsRequest ─────────────────────────────────────────────────────
/// POST /app/api/call/records — 拉取通话记录
///
/// [startFrom] Unix 时间戳增量拉取用0 表示全量。
/// [status] -1 = 全部状态。
class FetchCallLogsRequest
extends ApiRequestable<FetchCallLogsResponse> {
final int startFrom;
final int status;
const FetchCallLogsRequest({
this.startFrom = 0,
this.status = -1,
});
@override
String get path => ApiPaths.callRecords;
@override
HttpMethod get method => HttpMethod.post;
@override
Map<String, dynamic> get parameters => {
'start_from': startFrom,
'status': status,
};
@override
FetchCallLogsResponse? decodeResponse(dynamic response) {
final data = (response as dynamic).data;
if (data == null) return FetchCallLogsResponse(records: []);
if (data is List) {
return FetchCallLogsResponse(
records: data
.map((item) => CallLogDto.fromJson(item as Map<String, dynamic>))
.toList(),
);
}
if (data is Map<String, dynamic>) {
return FetchCallLogsResponse.fromJson(data);
}
return FetchCallLogsResponse(records: []);
}
}