feat: 配置挪到外边

This commit is contained in:
乘风
2025-12-23 11:52:06 +08:00
parent f8810f5199
commit 7a89b80c8f
3 changed files with 1019 additions and 1 deletions

View File

@@ -59,7 +59,7 @@ export const featureCategories: FeatureCard[] = [
"标签化精准推送",
"接待模式切换",
],
path: "/pc/powerCenter/ai-reception",
path: "/pc/commonConfigss",
},
// {
// id: "content-library",

View File

@@ -0,0 +1,495 @@
# 触客宝Touchkebao功能架构
> 本文档描述当前 Touchkebao 前端工程在 **页面路由、状态管理、数据访问、本地缓存和核心业务模块** 维度的功能架构,并附带 ASCII 线框图,便于沟通与扩展设计。
## 1. 技术栈与整体结构
- **基础框架**
- **React 18 + TypeScript**单页应用SPA
- **Vite**:构建与开发服务器(入口 `src/main.tsx`
- **UI 组件**
- PC 端:`antd`
- 移动端:`antd-mobile`
- **状态管理**`zustand`(含自定义持久化封装)
- **请求与数据**
- HTTP`axios` + 统一封装 `api/request.ts`
- 本地缓存:`Dexie`IndexedDB统一封装在 `utils/db.ts`
- **监控与稳定性**
- `@sentry/react`:初始化于 `main.tsx`,并通过 `App.tsx` 中的 `ErrorBoundary` 做兜底
- 自定义错误提示与 Toast`antd-mobile`
- **入口初始化流程**
- `src/main.tsx`
- 初始化 Sentry必须在其他逻辑前
- 设置 `dayjs` 中文
- 调用 `initializeDatabaseFromPersistedUser()`
- 从持久化的 user-store 中恢复上次登录用户
-`userId` 为维度初始化 Dexie 数据库
- 渲染根组件:
- `ConfigProvider`antd 中文配置)
- `QueryProvider`React Query 统一数据请求层)
- `App`
- `src/App.tsx`
- 使用 `Sentry.ErrorBoundary` 包裹整个应用
- 内部渲染:
- `AppRouter`(统一路由系统)
- `UpdateNotification`(版本更新提醒,支持自动刷新)
---
## 2. 路由 & 页面模块架构
路由集中于 `src/router` 目录,通过 `import.meta.glob` 动态加载模块。
### 2.1 路由整体
- **核心文件**
- `router/index.tsx`
- 使用 `import.meta.glob("./module/*.{ts,tsx}", { eager: true })` 自动导入所有路由模块
- 聚合得到 `routes: RouteObject[]`
-`auth: true` 的路由包装 `PermissionRoute`
- 追加通配符 `*``404`
- 使用 `BrowserRouter + useRoutes` 渲染
- `router/permissionRoute.tsx`
- 依赖 `useUserStore` 提供的 `user``isLoggedIn`
- 未登录时记录当前 `pathname + search`,跳转至 `/login?returnUrl=...`
- 当路由声明 `requiredRole` 时,校验 `user.isAdmin === 1`,否则跳转首页 `/`
- **路由模块划分**
- `router/module/common.tsx`
- `/``Index` 首页:根据终端类型跳转:
- 移动端 → `/mobile/dashboard`
- PC 端 → `/pc/weChat`
- `/login` → 登录页(免登录)
- `/guide` → 产品引导页(需要登录)
- `/init` → iframe 初始化页(嵌入其他系统对接)
- `router/module/pc.tsx`
- `/pc``CkboxPage`PC 端总布局,内含顶部导航)
- 子路由(均需要登录):
- `/pc/commonConfig` → 通用配置中心
- `/pc/dashboard` → 数据看板
- `/pc/weChat` → 微信客服工作台(核心聊天场景)
- `/pc/powerCenter` → 能力中心(导航页)
- `/pc/powerCenter/customer-management` → 客户管理
- `/pc/powerCenter/communication-record` → 沟通记录
- `/pc/powerCenter/content-management` → 内容管理 & 朋友圈发布
- `/pc/powerCenter/ai-training` → AI 训练/话术配置
- `/pc/powerCenter/auto-greeting` → 自动欢迎与自动打招呼
- `/pc/powerCenter/message-push-assistant` → 消息推送助手
- `/pc/powerCenter/message-push-assistant/create-push-task/:pushType` → 新建推送任务多步骤向导
- `/pc/powerCenter/data-statistics` → 数据统计
- `/pc/powerCenter/push-history` → 推送历史
- `router/module/mobile.tsx`
- `/profile` → 移动端个人中心/设置页(需要登录)
### 2.2 页面布局与导航
- **PC 布局(`components/Layout`**
- `Layout.tsx / LayoutFiexd.tsx`
- 支持传入自定义 header`NavCommon`
- 标准结构:
- 顶部:产品标题、用户信息、全局操作区
- 中间:左右结构(侧边栏 + 主内容)
- **PC 触客宝主页面(`pages/pc/ckbox/index.tsx` 及子模块)**
- `CkboxPage`
- 使用 `Layout` 作为整体页面容器
- header 为 `NavCommon`(标题“触客宝”等)
- 中间区域通过 `<Outlet />` 渲染子路由页面
---
## 3. 状态管理架构Zustand
### 3.1 Store 组织方式
- **统一出口**`src/store/index.ts`
- 导出:
- `useUserStore`(用户)
- `useAppStore`(应用级状态)
- `useSettingsStore`(个性化设置)
- `useWebSocketStore`WebSocket 连接与命令)
- 各种 `createPersistStore``persistUtils` 工具
- 提供:
- `getStores()`:一次性获取所有 store 状态
- `subscribeToAllStores()`:统一订阅所有 store 变更
- **业务模块 Store节选**
- `store/module/user.ts`
- 用户信息id、名字、角色、isAdmin 等)
- 登录状态 `isLoggedIn` 与 token
- 使用 `persist` 中间件,存入 localStorage
- `store/module/app.ts`
- 全局 loading、布局属性、全局配置开关等
- `store/module/settings.ts`
- 主题、表格密度等个性化设置
- `store/module/websocket/websocket.ts`
- WebSocket 连接状态(已连接/重连中等)
- `sendCommand` 封装(如 `CmdSendMessage`
- `store/module/weChat/*`
- 好友/群列表、客户、当前选中会话
- 聊天消息、分页、群成员
- AI 对话相关状态与逻辑(见 6 章节)
### 3.2 权限与登录态
- `PermissionRoute` 通过 `useUserStore` 读取:
- `isLoggedIn`:控制是否自动跳转登录页
- `user.isAdmin`:用于路由级别的角色校验
- 登录成功后:
- 持久化 user store以便刷新后仍可恢复
- `initializeDatabaseFromPersistedUser` 读取该信息并初始化 IndexedDB
---
## 4. 数据访问层API 封装)
### 4.1 通用请求封装(`api/request.ts`
- **Axios 实例配置**
- `baseURL`:优先取环境变量 `VITE_API_BASE_URL`,否则 `/api`
- 默认 `timeout`20 秒
- 默认请求头:`Content-Type: application/json`
- 请求拦截器:
-`useUserStore.getState()` 中读取 `token`
- 自动附加 `Authorization: Bearer <token>`
- **响应拦截器逻辑**
- 统一假设服务端返回结构:`{ code, success, msg, data }`
- 判断业务成功:
- 优先看 `code === 200`
-`success === true`
- 若都无,则认为是“透明透传”结构,直接返回原数据
- 业务失败处理:
- 如果 `code === 401`
- 清理本地 `token`
- 计算当前路径 `pathname + search`
- 重定向到 `/login?redirect=当前路径`
- 其它错误:
- 根据 `ERROR_SILENT_URLS` 白名单控制是否显示错误 Toast
- 使用 `Toast.show({content: msg || "接口错误"})` 提示
- 网络错误:
- 仍带白名单控制
- 默认展示 `"网络异常"` 提示
- **请求防抖机制**
- 使用 `debounceMap`Map<key, timestamp>)记录请求
- key 由 `method + url + data` 组成
- 默认间隔 `DEFAULT_DEBOUNCE_GAP = 1000ms`
- 可通过 `config.debounce = false` 关闭
- 高频接口白名单 `NO_DEBOUNCE_URLS`(好友/群列表、消息列表)不走防抖
- **统一导出函数**
- `request(url, data?, method = "GET", config?, debounceGap?)`
- 自动区分 GET`params`)与非 GET`data`
- 自动处理 FormData移除默认 `Content-Type`
### 4.2 文件上传封装(`api/common.ts`
- `uploadFile(file, uploadUrl = "/v1/attachment/upload")`
- 使用原生 `FormData` 封装文件
- 根据用户 token 设置 `Authorization`
- 返回后端 `data.url` 作为上传结果
### 4.3 业务 API 模块
- `api/ai.ts`
- `dataProcessing(params)`:新消息到达后进行预处理(发送到存客宝后台)
- `aiChat(params)`:根据上下文消息生成 AI 回复内容
- `api/module/wechat.ts`
- 聊天消息列表、群成员、好友/群列表等接口
- `api/module/group.ts`
- 分组/标签等管理类接口
---
## 5. 本地数据库架构IndexedDB + Dexie
### 5.1 数据库设计(`utils/db.ts`
- **数据库命名**
- 前缀:`CunkebaoDatabase`
- 最终库名:`CunkebaoDatabase_${userId}`
- 每个登录用户一个独立数据库,实现物理隔离
- **核心表结构(统一 friend / group 模型)**
- `ChatSession`(会话表)
- `serverId`:主键(使用服务端的 id
- `userId`:本地用户 ID用于多用户隔离
- `type``"friend" | "group"`
- 通用字段:`wechatAccountId, nickname, avatar, content, lastUpdateTime`
- 配置字段:`config.unreadCount, config.top`
- 排序字段:`sortKey`
- 扩展字段:`extendFields`(字符串 JSON
- `Contact`(统一联系人表)
- `serverId`:主键
- `userId`:用户 ID
- `type``"friend" | "group"`
- 通用信息:昵称、备注、头像、地区、签名、搜索 key 等
- 标签/分组:`groupId`
- 扩展结构:`extendFields`
- `ContactLabelMap`(联系人与标签映射表)
- `serverId``${contactId}_${labelId}`
- `labelId``contactId``contactType`
- 用于按标签筛选联系人、统计
- `UserLoginRecord`(登录记录表)
- `serverId`: `user_${userId}`
- `lastLoginTime``loginCount``lastActiveTime`
- **索引设计**
- 常用组合索引:
- `[userId+type]`
- `[userId+wechatAccountId]`
- `[userId+lastUpdateTime]`
- `sortKey / searchKey`
- 方便实现:
- 会话按更新时间与置顶排序
- 联系人/会话按关键字搜索
- 用户/标签多维度筛选
### 5.2 数据库管理与代理
- `DatabaseManager`
- 负责:
- 根据 `userId` 打开/切换数据库实例
- 维护 `currentDb``currentUserId`
- 提供 `ensureDatabase(userId)``getCurrentDatabase()` 等方法
- 支持检测是否已初始化(`isInitialized`)和关闭数据库(`closeCurrentDatabase`
- 启动恢复逻辑
- `initializeDatabaseFromPersistedUser()`
- 读取持久化 user-store`PERSIST_KEYS.USER_STORE`
- 解析得到 `user.id` 作为当前 userId
- 调用 `databaseManager.ensureDatabase(userId)` 初始化数据库
- 数据库代理对象
- 使用 `Proxy``db` 暴露为“当前数据库”的代理
- 调用侧无需感知内部的实例切换逻辑
- 通用 `DatabaseService<T>`
- 对 Dexie 的 `Table<T>` 封装:
- 增删改查、批量插入/更新、条件查询、分页、排序、统计
- 支持 `createWithServerId` / `createManyWithServerId`,与接口 ID 对齐
- 基于该服务构建:
- `chatSessionService`
- `contactUnifiedService`
- `contactLabelMapService`
- `userLoginRecordService`
---
## 6. 核心业务:微信客服工作台
### 6.1 页面结构(/pc/weChat
- 路由:`/pc/weChat``pages/pc/ckbox/weChat`
- 典型三栏布局:
1. 左侧侧边栏 `SidebarMenu`
- 会话列表 MessageList虚拟列表 + 未读数)
- 好友/群/客户切换
- 朋友圈入口
- 添加好友/新建会话入口
2. 中间聊天窗口 `ChatWindow`
- 消息列表(`VirtualizedMessageList`
- 文本、图片、语音、视频、小程序、红包、转账等多种消息类型组件
- 输入区 `MessageEnter`
- 文本输入框
- Emoji 表情(`EmojiSeclection`
- 文件/图片/语音/视频上传(复用 `components/Upload` 系列)
- 支持 AI 建议文案自动填充quoteMessageContent
- 辅助弹窗:
- 聊天记录搜索 `ChatRecordSearch`
- 转发消息 `TransmitModal`
- 待办/提醒 `TodoListModal`
3. 右侧信息栏 `ProfileCard`
- 客户画像与标签
- 朋友圈动态(`FriendsCicle`
- 快捷话术(`QuickWords`
- 与当前会话相关的附加信息
### 6.2 微信 Store`store/module/weChat/weChat.ts`
> 使用 `zustand + persist` 管理微信聊天域内的复杂状态。
- **状态主体**
- 当前对象与消息:
- `currentContract`: 当前选中的联系人/群(`ContractData | weChatGroup`
- `currentMessages`: 当前聊天消息数组
- `currentMessagesPage` / `currentMessagesPageSize` / `currentMessagesHasMore`
- `currentGroupMembers`: 当前群成员列表
- AI 相关:
- `aiQuoteMessageContent`: AI 接管配置值
- `quoteMessageContent`: AI 生成的文案草稿,用于填充输入框
- `isLoadingAiChat`: 是否正在生成 AI 回复
- 加载与 UI
- `messagesLoading` / `isLoadingData`
- `showCheckbox`:是否显示多选框
- `EnterModule`: `"common" | "multipleForwarding"`
- 朋友圈:
- `MomentCommon`: 朋友圈列表
- `MomentCommonLoading`: 加载标记
- **关键行为**
- `setCurrentContact(contract)`
- 切换当前会话:
- 清除 AI 请求队列与定时器
- 重置当前消息与分页状态
- 清除未读数(调用 `clearUnreadCount1/2`
- 拉取 AI 配置(`getFriendInjectConfig`
- 更新当前会话并调用 `loadChatMessages(true)` 拉取首屏消息
- `loadChatMessages(Init, pageOverride?)`
- 根据当前联系人是好友/群决定调用 `getChatMessages``getChatroomMessages`
- 统一消息列表结构,并按时间排序
- 处理分页信息page/limit/hasMore
- 首次加载群聊时同步拉取群成员列表
- `SearchMessage({From, To, keyword, Count})`
- 在当前会话内按时间 + 关键字搜索消息
- `receivedMsg(message)`
- 当 WebSocket 收到新消息时调用
- 若消息属于当前会话:
- 使用 **16ms 批量更新队列**,减少 re-render 次数
- 针对“对方发送的文字消息”,根据当前会话 `aiType` 触发 AI 逻辑:
- 先将消息加入 `pendingMessages` 队列
- 3 秒内无更多新消息,则批量发送到 `dataProcessing`
- 再调用 `aiChat` 获取 AI 返回内容
-`aiType === 2`AI 接管):
- 构造本地“发送中”消息并追加到 `currentMessages`
- 同时通过 WebSocket `CmdSendMessage` 实际发送
-`aiType === 1`AI 辅助):
- 将 AI 文案写入 `quoteMessageContent`MessageEnter 自动填充输入框
- `manualTriggerAi()`
- 用户手动点击“智能回复”等按钮,主动触发 AI 回复流程
---
## 7. 通用组件与能力模块
- **选择器组件**
- `AccountSelection / DeviceSelection / ContentSelection / FriendSelection / GroupSelection / PoolSelection / MemberSelection / TwoColumnSelection`
- 特点:
- 支持弹窗选择、多选、搜索
- 复用同一 API 与数据结构
- 应用于内容管理、推送任务、新建沟通等多处业务场景
- **上传组件**
- 位于 `components/Upload/*`
- 包含:
- `ImageUpload, FileUpload, VideoUpload, AudioUpload, AvatarUpload, ChatFileUpload, SimpleFileUpload, MainImgUpload`
-`uploadFile`(或独立 axios 调用)结合,支持多文件类型与进度回调
- **虚拟列表组件**
- `VirtualContactList, VirtualSessionList, VirtualMessageList, InfiniteList`
- 依托 `react-window` 等,实现上万级数据的高性能渲染
- **图表组件**
- `LineChart, LineChart2`
- 基于 `echarts-for-react`,主要用于看板与数据统计模块
---
## 8. ASCII 架构 & 页面线框图
### 8.1 系统高层功能架构(模块关系)
```text
+-------------------------------------------------------------+
| Touchkebao SPA |
| (React + TS + Vite, Antd, Antd-Mobile, Zustand, Dexie) |
+--------------------------+----------------------------------+
|
v
+-------------------------------------------------------------+
| App Shell |
| - App.tsx |
| - Sentry.ErrorBoundary |
| - AppRouter (BrowserRouter + useRoutes) |
+--------------------------+----------------------------------+
|
v
+-------------------------------------------------------------+
| Router 层 |
| - / -> Index (设备判断, 跳 /pc/weChat or /mobile) |
| - /login -> Login |
| - /guide -> Guide (auth) |
| - /init -> Iframe Init |
| - /pc/... -> PC 工作台模块 |
| - /profile -> Mobile Profile (auth) |
| - * -> 404 |
| 权限: PermissionRoute + useUserStore |
+--------------------------+----------------------------------+
|
v
+-------------------------------------------------------------+
| 业务模块PC |
| /pc (CkboxPage: Layout + NavCommon + <Outlet/>) |
| |- /pc/weChat -> 微信客服工作台 |
| |- /pc/dashboard -> 数据看板 |
| |- /pc/commonConfig -> 通用配置 |
| |- /pc/powerCenter/... -> 能力中心子模块 |
+--------------------------+----------------------------------+
|
v
+-------------------------------------------------------------+
| 状态与数据层 |
| - zustand stores |
| * user/app/settings/websocket |
| * weChat store (聊天状态 + AI 队列) |
| - api/request.ts (Axios 封装 + Token + 防抖 + Toast) |
| - api/ai.ts, api/module/wechat.ts, group.ts 等 |
| - Dexie IndexedDB (db.ts) |
| * chatSessions / contactsUnified / contactLabelMap |
| * userLoginRecords |
+-------------------------------------------------------------+
```
### 8.2 PC 微信客服工作台页面线框(/pc/weChat
```text
+-----------------------------------------------------------------------------------+
| 顶部导航 NavCommon |
| [ 触客宝 | 用户信息 | 其他入口 ] |
+-----------------------------------------------------------------------------------+
| |
| +------------------------+------------------------------------+----------------+ |
| | 左侧侧边栏 SidebarMenu | 中间聊天窗口 ChatWindow | 右侧信息栏 | |
| | | | ProfileCard 等 | |
| | - 账号/设备切换 | +------------------------------+ | | |
| | - 会话列表 MessageList| | 聊天消息列表 MessageList | | - 客户画像 | |
| | * 虚拟滚动 | | (VirtualizedMessageList) | | - 朋友圈动态 | |
| | * 未读数 | | - 文本/图片/语音/视频/... | | - 快捷话术 | |
| | - 好友/群/客户列表 | | - 各种系统消息类型组件 | | - AI 配置 | |
| | - 朋友圈入口 | +------------------------------+ | | |
| | - 添加好友/发起会话 | | 输入区 MessageEnter | | | |
| | | | - 文本输入 | | | |
| | | | - 表情 EmojiPicker | | | |
| | | | - 文件/图片/语音/视频上传 | | | |
| | | | - AI 建议文案 (quote…) | | | |
| +------------------------+------------------------------------+----------------+ |
| |
+-----------------------------------------------------------------------------------+
| 底部(可选):全局提示 / 更新提示 UpdateNotification |
+-----------------------------------------------------------------------------------+
```
### 8.3 PowerCenter 能力中心线框(简版)
```text
+----------------------------------------------------------------------------+
| /pc/powerCenter |
+----------------------------------------------------------------------------+
| 顶部Tab / 菜单导航 |
| [ 客户管理 ] [ 沟通记录 ] [ 内容管理 ] [ AI 训练 ] [ 自动欢迎 ] [ 推送助手 ] ... |
+----------------------------------------------------------------------------+
| 内容区:根据当前子路由渲染 |
| - 客户管理: 列表 + 筛选 + 详情侧滑抽屉 |
| - 沟通记录: 时间线/列表 + 搜索 |
| - 内容管理: 素材列表 + 编辑弹窗 + 预览 + 定时发布配置 |
| - AI 训练: 话术库配置、模型参数设置 |
| - 自动欢迎: 规则列表 + 开关 + 编辑 |
| - 推送助手: 创建推送任务多步骤向导(选择账号 -> 选人群 -> 配置内容 -> 发送) |
+----------------------------------------------------------------------------+
```
---
以上内容即为当前 Touchkebao 前端工程的高层功能架构说明,可作为后续架构演进、文档输出、分享 PPT 的基础材料。

View File

@@ -0,0 +1,523 @@
## 触客宝功能逻辑实现流程图
> 本文档基于《触客宝功能架构》整理关键业务链路的**实现流程**,以文本 + ASCII 流程图的形式展示前端在不同场景下的调用关系,便于开发/排查问题与架构沟通。
---
## 一、应用启动 & 初始化流程
### 1.1 启动总体流程
```text
浏览器访问页面
|
v
加载 index.html & Vite 入口脚本
|
v
执行 src/main.tsx
|
v
初始化 Sentry (initSentry)
|
v
initializeDatabaseFromPersistedUser()
|
+-- 读取 localStorage 中 USER_STORE
| |
| +-- 无用户信息 -> 不初始化 DB -> 返回 null
| |
| +-- 有用户信息 -> 解析 user.id -> ensureDatabase(userId)
| |
| +-- DatabaseManager 创建/打开 Dexie DB
|
v
创建 React Root
|
v
<ConfigProvider zhCN>
<QueryProvider>
<App />
</QueryProvider>
</ConfigProvider>
|
v
App.tsx 内部:
Sentry.ErrorBoundary 包裹 AppRouter + UpdateNotification
```
---
## 二、路由跳转 & 权限控制流程
### 2.1 路由解析与渲染
```text
AppRouter 渲染
|
v
import.meta.glob("./module/*.{ts,tsx}")
|
v
收集所有路由模块 default 导出路由数组
|
v
组合得到 routes[]
|
v
对每个 route:
如果 route.auth === true
-> 用 <PermissionRoute> 包裹
否则
-> 直接使用 route.element
|
v
追加 404 路由 { path: "*", element: <NotFound /> }
|
v
useRoutes(routes) 生成路由树并渲染
```
### 2.2 权限路由(登录 & 角色校验)
```text
用户访问受保护路由 (route.auth = true)
|
v
渲染 <PermissionRoute requiredRole={...}>
|
v
从 useUserStore 读取 user, isLoggedIn
|
+-- 若 !isLoggedIn 或 !user
| |
| +-- 记录当前路径 currentPath = pathname + search
| |
| +-- navigate("/login?returnUrl=" + encodeURIComponent(currentPath))
| |
| +-- 不渲染子组件 (return null)
|
+-- 若 requiredRole 存在 且 user.isAdmin !== 1
| |
| +-- navigate("/")
| +-- return null
|
+-- 否则 (已登录 & 权限满足)
|
+-- 渲染 children (真正的业务页面)
```
---
## 三、首页访问 & 端类型分流流程
### 3.1 `/` 首页逻辑
```text
用户访问 "/" 路径
|
v
渲染 IndexPage (pages/index.tsx)
|
v
useEffect(() => {
判断是否移动端:
- UA 匹配移动端关键字
- 或 window.innerWidth <= 768
})
|
+-- 若 isMobile === true
| |
| +-- navigate("/mobile/dashboard")
|
+-- 若 isMobile === false
|
+-- navigate("/pc/weChat")
|
v
页面本身只渲染简单 "首页" 占位,主要职责是分流
```
---
## 四、登录流程 & 本地缓存初始化
> 下面为前端视角,后端返回细节略去,仅关注状态/存储/跳转。
```text
用户在 /login 输入账号密码
|
v
点击登录按钮 -> 触发登录 API
|
v
调用 request("/login", formData, "POST")
|
v
Axios 请求拦截器:
- 若有 token 则附加 Authorization (首次通常无)
|
v
服务器返回登录结果 { code/success, data: { user, token } }
|
v
响应拦截器:
- 判断业务成功
- 返回 data (user, token)
|
v
前端登录逻辑:
- 调用 useUserStore.setState():
* 保存 user 信息
* 保存 token
* 标记 isLoggedIn = true
- 按 returnUrl 或默认路径跳转:
* 若 URL 中有 ?returnUrl=xxx -> navigate(xxx)
* 否则 -> navigate("/")
|
v
后续刷新页面:
- main.tsx 中 initializeDatabaseFromPersistedUser()
- 根据持久化 user.id 初始化对应 Dexie 数据库
```
---
## 五、PC 端微信客服工作台整体流程(/pc/weChat
### 5.1 页面三栏布局渲染流程
```text
用户访问 "/pc/weChat"
|
v
路由匹配 pc.tsx 中:
path: "/pc", element: <CkboxPage />, children: [...]
|
v
渲染 CkboxPage:
<Layout header={<NavCommon title="触客宝" />}>
<Outlet /> // 当前为 WeChatPage
</Layout>
|
v
WeChatPage 内部:
- 左侧 SidebarMenu (会话列表 / 联系人列表 / 朋友圈入口)
- 中间 ChatWindow (消息列表 + 输入框)
- 右侧 ProfileCard 等(客户画像/朋友圈/话术等)
```
### 5.2 选择会话 & 加载消息流程
```text
用户在 SidebarMenu 中点击某个好友/群聊
|
v
onContactClick(contact) 触发
|
v
useWeChatStore.setCurrentContact(contact)
|
v
setCurrentContact 内部逻辑:
1) 清除 AI 请求定时器 & 队列 (防止跨会话 AI 干扰)
2) 重置当前聊天状态:
- currentMessages = []
- currentMessagesPage = 1
- currentMessagesHasMore = true
- isLoadingAiChat = false
3) 构造 params:
- 单聊: wechatFriendId = contact.id
- 群聊: wechatChatroomId = contact.id
4) 调用 clearUnreadCount1/2 清空未读
5) 调用 getFriendInjectConfig 获取 AI 配置
-> 更新 aiQuoteMessageContent
6) 更新 currentContract = contact
并更新 currentMessagesRequestId
7) 调用 updateConfig({ id: contact.id, config: { chat: true } })
8) 调用 state.loadChatMessages(true) 拉取首屏消息
```
### 5.3 拉取聊天消息流程(首屏 & 翻页)
```text
调用 loadChatMessages(Init, pageOverride?)
|
v
从 store 读取:
- currentContract
- currentMessagesPage / PageSize / HasMore
- currentMessagesRequestId
|
+-- 若无 currentContract 或 id -> 直接返回
+-- 若 !Init 且 !HasMore -> 不再请求
+-- 若 messagesLoading && !Init -> 避免并发重复加载
|
v
计算 nextPage:
- Init === true ? 1 : (pageOverride || currentPage + 1)
limit = currentMessagesPageSize (默认 20)
|
v
构造请求参数 params:
- wechatAccountId: contact.wechatAccountId
- page, limit
- 单聊: wechatFriendId = contact.id
- 群聊: wechatChatroomId = contact.id
|
v
根据是否群聊:
- 群聊: 调用 getChatroomMessages(params)
- 单聊: 调用 getChatMessages(params)
|
v
normalizeMessages(response) 标准化为数组
|
v
sortMessagesByTime(messages) 统一按时间排序
|
v
resolvePaginationState(response, page, limit, listLength)
|
v
若 Init 且为群聊:
- 额外调用 getGroupMembers({ id: contact.id })
|
v
set(state => {
若 currentMessagesRequestId 未变化 && currentContract.id 未变化
- Init: currentMessages = sortedMessages
- 非 Init: currentMessages = [...sortedMessages, ...原有]
- 更新 currentGroupMembers (群聊首屏时)
- 更新 page / limit / hasMore
})
```
---
## 六、新消息接收 & AI 自动回复流程
### 6.1 新消息接收WebSocket → Store
```text
WebSocket 收到新消息 (CmdNewMessage)
|
v
WebSocket 层解析消息 -> 调用 useWeChatStore.receivedMsg(message)
|
v
receivedMsg(message) 内部:
1) 判断消息归属:
- getMessageId = message.wechatChatroomId || message.wechatFriendId
- isWechatGroup = !!message.wechatChatroomId
2) 判断是否为当前选中会话:
- 若 currentContract && currentContract.id == getMessageId
-> 进入“当前会话处理分支”
- 否则
-> 当前函数不更新会话列表MessageList 通过其他事件更新
```
### 6.2 当前会话消息的批量渲染优化
```text
当前会话处理分支:
|
v
将 message 推入 messageBatchQueue
|
v
若已有 messageBatchTimer:
-> clearTimeout(messageBatchTimer)
|
v
设置新的 messageBatchTimer (延迟 ~16ms):
|
v
定时器触发:
- 拷贝 messageBatchQueue -> messagesToAdd
- 清空 messageBatchQueue
- set(state => {
currentMessages = [...state.currentMessages, ...messagesToAdd]
})
|
v
实现一帧内多条消息合并更新,降低渲染压力
```
### 6.3 AI 自动回复决策流程
```text
在 receivedMsg 中 (仍然是当前会话分支):
|
v
若 message.msgType === 1 (文字) 且 !message.isSend
且 currentContract.aiType in [1, 2]
|
v
AI 触发逻辑:
1) 若存在 aiRequestTimer 或 currentAiGenerationId:
- 调用 clearAiRequestQueue("收到新消息")
- 清除上一次 AI 请求状态
2) 将当前 message 加入 pendingMessages 队列
3) set({ isLoadingAiChat: true })
4) 启动 aiRequestTimer (延迟 AI_REQUEST_DELAY=3000ms):
|
v
定时器回调:
- 复制 pendingMessages -> messagesToProcess
- 清空 pendingMessages, 清除 aiRequestTimer
- 生成 generationId = generateAiId()
- currentAiGenerationId = generationId
- 构造 dataProcessing 参数:
* type: "CmdNewMessage"
* wechatAccountId
* chatroomMessage 或 friendMessage = messagesToProcess
- 调用 dataProcessing(params)
- 若 dataProcessingResult 不为成功标志 (注意实际判断逻辑)
-> 取最后一条消息 lastMessage
-> 调用 aiChat({ friendId/getMessageId, wechatAccountId, message: lastMessage })
-> 得到 aiResponseContent
-> 若 currentAiGenerationId 仍等于 generationId (确保未被取消)
|
v
根据 aiType 分支:
- aiType === 2 (AI 接管模式)
* 构造本地发送消息 localMessage
* 将 localMessage append 到 currentMessages
* 调用 useWebSocketStore.sendCommand("CmdSendMessage", {...})
* set({ isLoadingAiChat: false })
* 清空 currentAiGenerationId
- aiType === 1 (AI 辅助模式)
* set({
quoteMessageContent: aiResponseContent,
isLoadingAiChat: false
})
* 清空 currentAiGenerationId
- 若中途任何错误:
-> 打印错误日志
-> set({ isLoadingAiChat: false })
-> currentAiGenerationId = null
```
### 6.4 手动触发 AI 回复流程
```text
用户在 UI 中点击“智能回复”按钮
|
v
调用 manualTriggerAi()
|
v
manualTriggerAi 逻辑:
1) 读取 currentContract, currentMessages
2) 若无 currentContract 或 currentMessages 为空 -> 返回 false
3) 校验 aiType in [1, 2],否则不触发
4) 若已有 aiRequestTimer 或 currentAiGenerationId:
-> clearAiRequestQueue("手动重新生成")
5) 从 currentMessages 中过滤最近 5 条对方文本消息 recentMessages
6) set({ isLoadingAiChat: true })
7) 生成 generationId, 记录到 currentAiGenerationId
8) 调用 aiChat({ friendId, wechatAccountId, message: lastMessage })
9) 根据 aiType:
- aiType === 2:
* 构造本地消息 localMessageappend 到 currentMessages
* sendCommand("CmdSendMessage", {...})
* set({ isLoadingAiChat: false })
- aiType === 1:
* updateQuoteMessageContent(aiResponseContent)
* set({ isLoadingAiChat: false })
10) 清空 currentAiGenerationId
```
---
## 七、消息搜索 & 历史记录查看流程
```text
用户在聊天窗口打开“聊天记录搜索”面板
|
v
选择时间范围 (From, To)、输入关键字 keyword、设置数量 Count
|
v
调用 useWeChatStore.SearchMessage({ From, To, keyword, Count })
|
v
SearchMessage 内部:
1) set({ messagesLoading: true })
2) 判断当前会话是群聊还是单聊
3) 构造 params:
- wechatAccountId
- keyword
- From, To
- page = 1
- limit = Count
- 单聊: wechatFriendId
- 群聊: wechatChatroomId
4) 调用对应 API:
- 群聊: getChatroomMessages(params) + getGroupMembers({ id })
- 单聊: getChatMessages(params)
5) 对返回结果:
- normalizeMessages -> sortMessagesByTime
- set({
currentMessages,
currentGroupMembers (群聊),
currentMessagesPage = 1,
currentMessagesHasMore = false,
currentMessagesPageSize = Count
})
6) finally: set({ messagesLoading: false })
```
---
## 八、PowerCenter 能力中心典型流程(示例:创建消息推送任务)
> 这里只给出“消息推送助手”的高层流程,实际每个步骤内部还会调用共用选择组件和上传组件。
```text
用户访问 "/pc/powerCenter/message-push-assistant"
|
v
展示推送任务列表 + “新建推送任务”按钮
|
v
点击“新建推送任务”
|
v
navigate("/pc/powerCenter/message-push-assistant/create-push-task/:pushType")
|
v
渲染 CreatePushTask 页面 (多步骤向导):
Step 1: StepSelectAccount
- 使用 AccountSelection / DeviceSelection 组件
- 选择推送使用的微信/设备账号
Step 2: StepSelectContacts
- 调用联系人/分组相关 API
- 使用 FriendSelection / GroupSelection / PoolSelection 等组件
- 支持搜索/筛选与多选
Step 3: StepPushParams
- 设置推送参数 (时间、频次、规则等)
Step 4: StepSendMessage
- 选择消息内容:
* 直接输入文本
* 从内容库选择: ContentSelection + 内容管理 API
- 支持图片/文件/视频等资源上传 (复用 Upload 组件)
|
v
完成配置 -> 点击“创建任务”
|
v
调用创建任务 API
|
v
成功后:
- 弹出成功提示
- 跳转回任务列表或详情页
- 列表数据可能通过 React Query 或手动刷新获取最新状态
```
---
以上流程图覆盖了应用启动、路由与权限、登录、PC 微信客服工作台、AI 自动回复、消息搜索以及能力中心中一个典型场景的端到端逻辑,可作为排查问题和设计新功能时的“全局参考图”。
如需更详细的**时序图Sequence Diagram**或针对某个子模块(例如 IndexedDB 同步、WebSocket 重连策略等)的专门流程图,可以在此文档基础上再拆分子章节。