From 7ae26b3368ff635cd25819bcc763cc0d93b2737d Mon Sep 17 00:00:00 2001 From: pp-bot Date: Tue, 24 Mar 2026 20:32:27 +0900 Subject: [PATCH] =?UTF-8?q?docs(recent-calls):=20=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E8=A1=A5=E5=85=85=EF=BC=88#42~#44=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 Doc/recent_calls_architecture.md: - 功能范围、目录结构、数据流图 - API 端点 /app/api/call/records 字段映射表 - 通话状态枚举(0-6)与未接来电判断逻辑 - ViewModel 设计、UI 结构、路由说明 - 设计决策(无用户名映射、全量拉取、离线可用) Co-Authored-By: Claude Sonnet 4.6 --- Doc/recent_calls_architecture.md | 166 +++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Doc/recent_calls_architecture.md diff --git a/Doc/recent_calls_architecture.md b/Doc/recent_calls_architecture.md new file mode 100644 index 0000000..aa07785 --- /dev/null +++ b/Doc/recent_calls_architecture.md @@ -0,0 +1,166 @@ +# 最近呼叫(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 → 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 = Busy(CallOptBusy) +// 4 = Cancelled(CallOptCancel) +// 5 = Timeout(CallTimeOut) +// 6 = Declined(CallBusy) + +// 未接来电: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 { + void setTab(int index) // 切换 Tab + Future loadCallLogs() // 拉取 + 写入 DB + Future 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 → RecentCallsPage(parentNavigatorKey=_rootKey) +``` + +`auth_guard.dart` switch 已补充 `settingsRecentCalls` case。 + +--- + +## 9. 设计决策 + +- **无用户名映射**:当前 CallLog 实体只有 UID,无昵称。显示 `@J{uid}` 占位, + 后续可接入联系人 Repository 做名称查询。 +- **增量拉取策略**:当前实现 `start_from=0`(全量),首次打开可能慢; + 后续可从本地最新 `updatedAt` 做增量同步(参考 CallLogMgr.loadRemoteCallLog)。 +- **离线可用**:数据先写 DB,UI 监听 `allCallLogsProvider`(DB Stream), + 无网络时仍能展示缓存记录。