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

4.5 KiB
Raw Permalink Blame History

最近呼叫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. 通话状态枚举

// 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 设计

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 监听 allCallLogsProviderDB Stream 无网络时仍能展示缓存记录。