Files
customer-im-client-dev/Doc/recent_calls_architecture.md
pp-bot 7ae26b3368 docs(recent-calls): 架构文档补充(#42~#44)
新增 Doc/recent_calls_architecture.md:
- 功能范围、目录结构、数据流图
- API 端点 /app/api/call/records 字段映射表
- 通话状态枚举(0-6)与未接来电判断逻辑
- ViewModel 设计、UI 结构、路由说明
- 设计决策(无用户名映射、全量拉取、离线可用)

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

167 lines
4.5 KiB
Markdown
Raw 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.
# 最近呼叫Recent Calls— 架构文档
> 对应 Gitea issues #46-#48内部编号 #42-#44
> 参考实现:`im-client-im-dev` lib/views/call_log/ + lib/managers/call_log_mgr.dart
---
## 1. 功能范围
| Issue | 功能 | 状态 |
|-------|------|------|
| #42 | 最近呼叫列表页 UI全部/未接 Tab + CallLogTile | ✅ 已实现 |
| #43 | 通话记录 API 接入FetchCallLogsRequest + UseCase | ✅ 已实现 |
| #44 | RecentCallsViewModel + 路由接入 | ✅ 已实现 |
---
## 2. 目录结构
```
features/settings/
├── di/
│ └── settings_providers.dart # 新增 fetchCallLogsUseCaseProvider
├── presentation/
│ └── recent_calls_view_model.dart # RecentCallsViewModel + RecentCallsState
├── usecases/
│ └── fetch_call_logs_usecase.dart # POST /app/api/call/records → DB
└── view/
└── recent_calls_page.dart # RecentCallsPage + TabBar + _CallLogTile
data/remote/
└── call_log_request.dart # FetchCallLogsRequest + CallLogDto
app/router/
├── app_route_name.dart # 新增 settingsRecentCalls
├── app_router.dart # 新增 GoRoute
└── guards/auth_guard.dart # 新增 case settingsRecentCalls
```
---
## 3. 数据流
```
RecentCallsPage (build)
└─ ref.watch(allCallLogsProvider) ← DB Stream实时
└─ RecentCallsViewModel.loadCallLogs()
└─ FetchCallLogsUseCase.execute()
└─ POST /app/api/call/records (start_from=0, status=-1)
├─ 响应 List<CallLogDto> → CallLog Domain
└─ CallLogRepository.insertOrReplaceCallLogs()
└─ Drift DB → allCallLogsProvider 自动刷新 → UI
进入页面:
└─ RecentCallsViewModel.markAllRead()
└─ CallLogRepository.markAllAsRead()
```
---
## 4. API 端点
| 操作 | Method | Path | 参数 |
|------|--------|------|------|
| 拉取通话记录 | POST | `/app/api/call/records` | `start_from` (int, 时间戳), `status` (-1=全部) |
### 响应字段映射
| 服务端字段 | Dart 字段 | 类型 |
|-----------|-----------|------|
| `rtc_channel_id` | `id` (String) | String |
| `inviter_id` / `caller_id` | `callerId` | int? |
| `receiver_id` | `receiverId` | int? |
| `chat_id` | `chatId` | int? |
| `duration` | `duration` | int? (秒) |
| `video_call` | `videoCall` | int? (0=语音, 1=视频) |
| `created_at` | `createdAt` | int? (秒级时间戳) |
| `updated_at` | `updatedAt` | int? |
| `ended_at` | `endedAt` | int? |
| `status` | `status` | int? |
| `is_read` | `isRead` | int? (0/1) |
---
## 5. 通话状态枚举
```dart
// status 值含义(参考 im-client-im-dev CallEvent
// 0 = Ringing/Pending
// 1 = Connected
// 2 = Ended正常结束
// 3 = BusyCallOptBusy
// 4 = CancelledCallOptCancel
// 5 = TimeoutCallTimeOut
// 6 = DeclinedCallBusy
// 未接来电callerId != currentUid AND status in {3, 4, 5, 6}
```
---
## 6. ViewModel 设计
```dart
class RecentCallsState {
final int tabIndex; // 0=全部, 1=未接来电
final bool isLoading;
final String? error;
}
class RecentCallsViewModel extends Notifier<RecentCallsState> {
void setTab(int index) // 切换 Tab
Future<void> loadCallLogs() // 拉取 + 写入 DB
Future<void> markAllRead() // 标记已读
}
```
---
## 7. UI 结构
### RecentCallsPage
```
Scaffold
AppBar: 「最近呼叫」
Column
TabBar全部 / 未接来电)
Expanded
TabBarView
AllCallsTab → allCallLogsProvider 全部
MissedTab → 按 isMissed 过滤
```
### _CallLogTile
```
ListTile
leading: _CallTypeIcon电话/摄像头/红色, 40pt 圆形)
title: 对方 UID 显示("@J{uid}"
subtitle: 通话类型文字 + 时长
trailing: 相对时间
```
**未接来电判断**`callerId != currentUid` AND `status in {3,4,5,6}`
---
## 8. 路由
```
/settings/recent-calls → RecentCallsPageparentNavigatorKey=_rootKey
```
`auth_guard.dart` switch 已补充 `settingsRecentCalls` case。
---
## 9. 设计决策
- **无用户名映射**:当前 CallLog 实体只有 UID无昵称。显示 `@J{uid}` 占位,
后续可接入联系人 Repository 做名称查询。
- **增量拉取策略**:当前实现 `start_from=0`(全量),首次打开可能慢;
后续可从本地最新 `updatedAt` 做增量同步(参考 CallLogMgr.loadRemoteCallLog
- **离线可用**:数据先写 DBUI 监听 `allCallLogsProvider`DB Stream
无网络时仍能展示缓存记录。