feat(shangwutong): sync classifications and update conversations

This commit is contained in:
2026-09-11 17:08:18 +08:00
parent a06d4f4199
commit 31f157a4f5
28 changed files with 2541 additions and 232 deletions
@@ -0,0 +1,251 @@
# 商务通分类同步与会话分类配置实施计划
> 日期:2026-09-11
> 状态:首版实现完成,待真实商务通账号灰度验证
> 关联调研:[`docs/research/2026-09-11-shangwutong-pc-classification-protocol.md`](../research/2026-09-11-shangwutong-pc-classification-protocol.md)
## 1. 目标与边界
为每个对接的商务通账号提供:
1. 从商务通读取对话分类和客户颜色分类定义;
2. 在对应收件箱提供“同步商务通分类”按钮;
3. 在商务通会话右侧客户信息中提供两个独立下拉框;
4. 修改后通过 Connector 下发到商务通;
5. GoChat 不预设、创建、编辑或删除商务通分类定义。
明确不做:
- 不把商务通分类写入 GoChat 原生标签定义表;
- 不把商务通颜色分类映射为 CRM `CategoryInFo``ClientLabelConfig`
- 不为未同步的收件箱生成默认分类;
- 不在浏览器直接持有或调用商务通凭据。
## 2. 已确认的商务通协议
### 2.1 分类定义
```text
POST {serverurl}/oc/SiteSetting.aspx?sn={sn}&siteid={siteid}&act=load
```
对话分类:
```text
sidkind_share = id,txt,iconindex|id,txt,iconindex|...
```
客户颜色分类:
```text
colorkind0_share = id|id|...
colorkind1_share = name|name|...
```
保存/同步请求返回 `r=ok``r=load ok`。GoChat 只读取定义,不调用保存配置接口。
### 2.2 会话分类修改
对话分类:
```text
POST {serverurl}/oc/SetSidKind.aspx
sid, oname, siteid, kind
```
客户颜色分类:
```text
POST {serverurl}/oc/changecolor.aspx
sid, oname, siteid, c0, c1, cid
```
清除颜色分类的 `RESET` 语义暂不作为首版 UI 能力暴露。
## 3. 总体架构决定
采用“GoChat 按收件箱持久化远程分类缓存”的方案:
```text
前端收件箱同步按钮
-> GoChat 管理 API
-> 已签名的 GoChat -> Connector webhook 控制事件
-> Connector 使用账号会话读取商务通
-> Connector 回调 GoChat
-> GoChat 按 inbox_id 更新缓存
-> 前端刷新列表
```
会话修改采用现有 Connector 出站操作队列:
```text
前端下拉修改
-> GoChat 校验 inbox/conversation/sid/cid
-> 持久化 outbound operation
-> Connector 读取 operation
-> 调用 SetSidKind.aspx 或 changecolor.aspx
-> 回传结果
-> GoChat 更新会话分类确认状态
```
分类缓存按 `inbox_id` 隔离。同步失败保留旧缓存,并返回可展示的错误状态。
## 4. 分步实施清单
### 阶段 1:协议值对象与 Connector 商务通客户端
- [x] 增加对话分类、客户颜色分类值对象;
- [x] 实现 `SiteSetting.aspx` 读取和严格解析;
- [x] 实现 `SetSidKind.aspx`
- [x] 实现 `changecolor.aspx`
- [x] 保留已有认证、超时、响应状态和不确定结果处理;
- [x] 为分隔符、空值、重复 ID、平行数组长度不一致增加测试。
阶段验证:`cd channels/shangwutong && go test ./internal/swt`
### 阶段 2GoChat 分类缓存与 Connector 回调
- [x] 新增按 `inbox_id` 唯一的分类缓存模型及迁移;
- [x] 保存对话分类、客户颜色分类、同步时间、同步状态和最后错误;
- [x] 增加管理员读取分类缓存 API
- [x] 增加管理员触发同步 API
- [x] 增加 Connector 结果回调 API,验证账号、收件箱和幂等键;
- [x] 扩展已签名 webhook 事件 `classification_sync_requested`
- [x] Connector 完成远程读取后回调 GoChat;
- [x] 失败时不覆盖上一次成功缓存。
### 阶段 3:会话分类出站操作
- [x] 扩展 GoChat 会话分类 API
- [x] 校验会话属于商务通收件箱;
- [x] 从会话/联系人渠道元数据取得 `sid``cid`
- [x]`set_chat_kind``set_customer_color` 写入现有出站队列;
- [x] Connector 出站 worker 调用对应商务通接口;
- [x] 增加操作结果回调和幂等处理;
- [x] 只有商务通确认成功后更新确认值,失败/不确定保留状态并提示。
### 阶段 4:前端收件箱设置
- [x] 在商务通收件箱设置中增加“同步商务通分类”按钮;
- [x] 展示对话分类和客户分类同步结果;
- [x] 同步中禁用按钮;
- [x] 同步失败保留旧列表;
- [x] 不提供分类新增、编辑、删除控件。
### 阶段 5:前端会话右侧客户信息
- [x] 仅商务通会话显示“商务通分类”区块;
- [x] 增加对话分类下拉;
- [x] 增加客户分类下拉;
- [x] 未同步、失效或缺少 `cid` 时安全禁用;
- [x] 提交中禁用对应控件;
- [x] 根据远程确认结果更新或恢复值(轮询会话确认属性,超时显示处理中);
- [x] 不复用 GoChat 原生标签组件。
### 阶段 6:验证与文档
- [x] Go 单元测试与全模块编译;
- [x] SQLite migration/build/test(迁移已通过最小 SQLite 外键/建表冒烟验证);
- [x] 前端 production build
- [x] Connector 协议 mock 测试;
- [x] 更新调研文档中的实现状态;
- [x] 检查无凭据泄漏、跨 inbox 读取和跨账号分类串用。
## 5. API 草案
### 5.1 读取缓存
```http
GET /api/v1/accounts/:account_id/inboxes/:inbox_id/shangwutong/classifications
```
### 5.2 触发同步
```http
POST /api/v1/accounts/:account_id/inboxes/:inbox_id/shangwutong/classifications/sync
```
### 5.3 修改会话分类
```http
PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/shangwutong-classifications
```
请求体只允许一个修改目标:
```json
{"chat_kind_id": 2}
```
或:
```json
{"customer_color_id": 1}
```
### 5.4 Connector 回调
Connector 专用回调不暴露给普通前端用户,沿用 Connector 平台鉴权和收件箱授权边界。
## 6. 数据模型草案
分类定义不是 GoChat 原生标签,建议独立表:
```text
shangwutong_classification_caches
- inbox_id unique
- conversation_kinds JSON
- customer_color_kinds JSON
- sync_status pending/succeeded/failed
- synced_at nullable
- last_error_code nullable
- last_error_message nullable
- created_at
- updated_at
```
分类值只保留商务通远程 ID、名称及图标索引;不生成 GoChat `Tag` 记录。
## 7. 验收标准
- 不同步的商务通收件箱不显示伪造分类;
- 同步成功后两个列表均可按收件箱读取;
- 同步失败不会清空上一次成功结果;
- 会话右侧只对商务通显示两个独立下拉;
- 选择对话分类最终请求 `SetSidKind.aspx``kind`
- 选择客户分类最终请求 `changecolor.aspx``c0/c1/cid`
- `CategoryID``LabelID` 不参与上述实时会话接口;
- 重复点击和重试不会产生冲突修改;
- 远程失败或不确定时 UI 不显示虚假的成功状态;
- 不同商务通收件箱的分类 ID 不互相串用。
## 8. 风险与暂缓项
1. 商务通 PC 端分类读取响应存在旧式字符串协议,必须拒绝坏格式,不应静默错配两个颜色数组;
2. `cid` 是客户级标识,客户颜色修改的跨会话作用范围需灰度账号验证;
3. Connector 到 GoChat 的同步回调必须使用幂等键,避免 webhook 重试覆盖更新状态;
4. 首版不暴露清除客户分类按钮,待 `RESET` 完整协议验证后再增加;
5. 分类定义同步和会话分类修改必须分开建模,不能以“同步成功”代替“会话修改成功”。
## 9. 首版实现记录
已落地的主要文件:
- Connector 协议:`channels/shangwutong/internal/swt/classifications.go`
- Connector 回调客户端:`channels/shangwutong/internal/gochat/classifications.go`
- Connector 会话操作:`channels/shangwutong/internal/delivery/outbound.go`
- Connector webhook 分发:`channels/shangwutong/internal/httpapi/server.go`
- GoChat 缓存模型与迁移:`backend/internal/model/channel_shangwutong_classification_cache.go``backend/migrations/000087_*`
- GoChat API 与任务:`backend/internal/handler/api/v1/shangwutong_connector_handler.go``backend/internal/service/shangwutong_webhook_delivery.go`
- 前端同步按钮:`frontend/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/ShangwutongConfiguration.vue`
- 前端会话下拉:`frontend/app/javascript/dashboard/routes/dashboard/conversation/ShangwutongClassifications.vue`
验证结果:
```text
cd channels/shangwutong && go test ./... && go vet ./...
cd backend && GOCHAT_TEST_DB=sqlite go test ./...
cd frontend && pnpm build
```
均已通过。前端修改文件的 ESLint 无错误;现有设置组件保留一条既有的动态 i18n key warning。真实商务通账号的灰度协议验证仍待安排。
@@ -0,0 +1,611 @@
# 商务通 PC 端分类协议调研结论
> 日期:2026-09-11
> 状态:PC 端逆向调研阶段性结论;GoChat 兼容实现已按关联计划落地首版
> 软件:商务通 PC 端(部署程序名 `LiveReception.exe`
## 1. 调研范围
本次确认商务通 PC 端以下三类能力:
1. 获取对话分类列表;
2. 获取客户分类列表;
3. 为当前会话设置对话分类和客户分类。
同时核对 CRM 中的客户主分类、客户标签,避免将它们与实时会话中的“客户分类”混为一谈。
## 2. 最终结论
商务通 PC 端存在两套独立的实时会话分类:
| 业务名称 | 商务通内部概念 | 会话字段 | 配置/操作接口 |
| --- | --- | --- | --- |
| 对话分类 | Chat Kind / Sid Kind | `visitors.chatkind` | `sidkind_share``oc/SetSidKind.aspx` |
| 客户分类 | 颜色/客户颜色分类 | `visitors.colors` | `colorkind0_share``colorkind1_share``oc/changecolor.aspx` |
CRM 另有独立的数据模型:
- `CategoryInFo`:客户主分类;
- `ClientLabelConfig`:客户标签定义;
- `ClientLabelInFo`:客户与标签的关联。
在反编译得到的实时会话配置链路中,没有发现 `CategoryID``LabelID` 被提交给 `SetSidKind.aspx``changecolor.aspx`。因此,商务通实时会话中的“客户分类”应实现为颜色分类,不能直接映射成 GoChat 的 CRM Contact Label。
## 3. 证据与逆向环境
### 3.1 目标程序
```text
远程主机:rogee@10.1.1.101
程序目录:C:\Users\Rogee\AppData\Roaming\ZoosNet\LiveReception
程序:LiveReception.exe
```
目标程序为旧版 32 位 .NET 程序,字符串经过运行时混淆。64 位 PowerShell 和 Mono 无法稳定执行其解密逻辑,最终使用远程 32 位 PowerShell/.NET Framework 解码字符串。
### 3.2 反编译目录
```text
/tmp/live-source
```
主要证据文件:
```text
/tmp/live-source/LiveWS.LR/PublicData.cs
/tmp/live-source/LiveWS.LR/MainFrm.cs
/tmp/live-source/LiveWS.LR.Dialog/ChatKindSettingFrm.cs
/tmp/live-source/LiveWS.LR.Dialog/SetChatKindFrm.cs
/tmp/live-source/LiveWS.LR.Dialog/CustomerClassificationFrm.cs
/tmp/live-source/LiveWS.LR.Dialog/CustomerClassificationFrm_Ext.cs
/tmp/live-source/CRM.Manage.DataManage/CategoryInFoManage.cs
/tmp/live-source/CRM.Manage.DataManage/ClientLabelConfigManage.cs
/tmp/live-source/CRM.Manage.DataManage/SQLiteCRMDbContext.cs
```
## 4. 对话分类列表
### 4.1 配置页面和内存结构
配置页面:
```text
LiveWS.LR.Dialog.ChatKindSettingFrm
```
内存数据集:
```text
LiveWS.LR.DS.ChatKindDS
```
`ChatKindDS` 的核心列:
| 列 | 含义 |
| --- | --- |
| `id` | 对话分类 ID |
| `txt` | 分类显示名称 |
| `iconindex` | UI 图标索引 |
`PublicData.ReloadChatKind(string kindStr)` 将服务端字符串解析为 `ChatKindDS` 行,并提供:
- `FindChatKind(int kid)`:按 ID 查找;
- `GetChatKindText(...)`:获取显示名称;
- `CheckChatKind(...)`:校验分类;
- `CheckChatKindText(...)`:校验名称。
### 4.2 列表读取接口
基础请求:
```http
POST {serverurl}/oc/SiteSetting.aspx?sn={sn}&siteid={siteid}&act=load
```
请求会附带站点配置所需的 `sn`、站点 ID 以及共享设置字段。
读取字段:
```text
sidkind_share
```
返回标记:
```text
r = load ok
```
`sidkind_share` 的格式:
```text
id,txt,iconindex|id,txt,iconindex|...
```
例如:
```text
1,在线咨询,0|2,售后服务,1
```
实际解析规则是:
1. 先按 `|` 拆分记录;
2. 每条记录再按 `,` 拆分;
3. 第一个字段写入 `id`
4. 第二个字段写入 `txt`
5. 第三个字段存在时写入 `iconindex`
### 4.3 列表保存接口
仍使用 `SiteSetting.aspx`
```http
POST {serverurl}/oc/SiteSetting.aspx?sn={sn}&siteid={siteid}&act=save
```
将当前 `ChatKindDS` 行重新序列化为:
```text
id,txt,iconindex|id,txt,iconindex|...
```
然后提交:
```text
sidkind_share=<serialized-value>
```
`ChatKindSettingFrm` 的新增、编辑、删除最终都归并为这一次共享站点配置保存。
## 5. 实时会话客户分类列表
### 5.1 业务含义
商务通 PC 端“客户分类”页面对应的是颜色/图标分类,不是 CRM 的 `CategoryInFo`
相关页面:
```text
LiveWS.LR.Dialog.CustomerClassificationFrm_Ext
LiveWS.LR.Dialog.CustomerClassificationFrm
```
`CustomerClassificationFrm_Ext` 用于维护分类定义;`CustomerClassificationFrm` 用于在当前会话上选择一个分类。
### 5.2 配置字段
颜色分类通过同一个站点设置接口维护:
```http
POST {serverurl}/oc/SiteSetting.aspx?sn={sn}&siteid={siteid}&act=load
```
读取两个平行数组:
```text
colorkind0_share
colorkind1_share
```
含义:
| 字段 | 含义 |
| --- | --- |
| `colorkind0_share` | 颜色/图标索引,作为分类值 |
| `colorkind1_share` | 分类显示名称 |
格式:
```text
colorkind0_share = 0|1|2
colorkind1_share = 普通客户|重要客户|VIP
```
两个数组按相同下标配对。PC 端将它们加载到:
```text
PublicData.colorkinds0
PublicData.colorkinds1
```
选择页面用这些数组填充 ImageCombo,颜色/图标索引来自 `colorkinds0`,显示文本来自 `colorkinds1`
### 5.3 保存颜色分类定义
保存请求:
```http
POST {serverurl}/oc/SiteSetting.aspx?sn={sn}&siteid={siteid}&act=save
```
保存时分别把选中的索引和名称用 `|` 连接:
```text
colorkind0_share=<index>|<index>|...
colorkind1_share=<name>|<name>|...
```
成功后更新内存中的 `PublicData.colorkinds0/1`。关闭页面时会清理这两个临时内存数组。
## 6. 为会话设置对话分类
### 6.1 PC 端调用链
```text
MainFrm.qpSJrYUIY8S
-> 读取当前 visitors DataRow
-> 打开 SetChatKindFrm
-> 取得 SetChatKindFrm.chatkindStr
-> 创建 HttpRequestPM
-> 加入 MainFrm.TaskList
-> 后台 worker 执行 changesidkind
```
当前会话原值来自:
```text
visitors.chatkind
```
`SetChatKindFrm` 只负责展示和返回所选分类,不直接发送 HTTP 请求。
### 6.2 内部任务数据
`MainFrm` 创建的任务等价于:
```text
cmd = changesidkind
sid = 当前会话 sid
otherdata = [
newChatKind,
cookies,
weixinid,
ServiceURL,
"1"
]
```
其中:
- `newChatKind`:所选对话分类 ID
- `cookies`:访客/客户级标识;
- `weixinid``ServiceURL`:渠道侧同步所需的会话元数据;
- 最后的 `"1"`PC 端内部的即时更新标记。
### 6.3 实际 HTTP 接口
后台命令分发器将 `changesidkind` 映射到:
```http
POST {serverurl}/oc/SetSidKind.aspx
```
核心参数:
```text
sid = 会话 ID
oname = 当前操作员名称
siteid = 站点 ID
kind = newChatKind
```
成功响应使用:
```text
r = ok
```
此接口只设置会话分类,不设置 CRM 客户主分类或客户标签。
### 6.4 本地数据
生成的 `LRLocalDS.visitors` 数据表中已确认:
```text
chatkind : short
```
PC 端发送任务后,当前会话的最终显示由请求结果、渠道同步消息或会话数据刷新完成;在 `MainFrm` 的发送入口中没有发现直接写入数据库的简单赋值语句。
## 7. 为会话设置客户颜色分类
### 7.1 PC 端调用链
```text
MainFrm.bFgJzrTJoY3
-> 读取当前 visitors DataRow
-> 打开 CustomerClassificationFrm
-> 取得 ImageCombo 当前索引和文本
-> 创建 HttpRequestPM
-> 加入 MainFrm.TaskList
-> 后台 worker 执行 changecolor
```
当前分类值来自:
```text
visitors.colors
```
`CustomerClassificationFrm` 会根据 `PublicData.colorkinds0/1` 生成可选项,并根据当前 `colors` 选中对应颜色。
### 7.2 内部任务数据
`MainFrm` 创建的任务等价于:
```text
cmd = changecolor
sid = 当前会话 sid
otherdata = [
colorId,
colorName,
cookies,
weixinid,
ServiceURL,
"1"
]
```
其中:
- `colorId`ImageCombo 的颜色/图标索引;
- `colorName`:当前分类名称;
- `cookies`:访客/客户级 ID
- `weixinid``ServiceURL`:渠道侧同步所需元数据。
### 7.3 实际 HTTP 接口
后台命令分发器将 `changecolor` 映射到:
```http
POST {serverurl}/oc/changecolor.aspx
```
核心参数:
```text
sid = 会话 ID
oname = 当前操作员名称
siteid = 站点 ID
c0 = colorId
c1 = colorName
cid = cookies
```
清除客户分类时还会走 `RESET` 分支。
成功响应使用:
```text
r = ok
```
成功后 PC 端还可能通过渠道管理器发送颜色/标记同步消息;这与商务通服务端的 `changecolor.aspx` 请求是两个层次。
### 7.4 本地数据
生成的 `LRLocalDS.visitors` 数据表中已确认:
```text
colors : int
```
颜色名称不是独立的会话字段,而是通过 `colors``PublicData.colorkinds0/1` 反查显示。
## 8. 对话分类与客户分类不是一个接口
商务通 PC 端没有发现一个同时设置两种分类的接口。实际需要分别调用:
```text
SetSidKind.aspx -> 对话分类
changecolor.aspx -> 客户颜色分类
```
对应关系:
| 需求 | 配置列表 | 会话操作 | 会话字段 |
| --- | --- | --- | --- |
| 对话分类 | `sidkind_share` | `SetSidKind.aspx` | `chatkind` |
| 客户分类 | `colorkind0_share` + `colorkind1_share` | `changecolor.aspx` | `colors` |
GoChat 兼容实现不能把 `sidkind_share` 当成会话更新参数,也不能把 `CategoryID` 当成 `c0`
## 9. CRM 客户主分类与客户标签
### 9.1 CRM 客户主分类
模型:
```text
CRM.Models.CategoryInFo
```
核心字段:
```text
ID
Title
Comment
ColorValue
```
读取管理器:
```text
CRM.Manage.DataManage.CategoryInFoManage
```
全部分类:
```text
CategoryInFoManage.Get()
```
过滤条件为:
```sql
TokenTime >= 0
ORDER BY CreateTime DESC
```
有效分类:
```text
CategoryInFoManage.GetStatusIsTrue()
```
过滤条件为:
```sql
Status = 1
AND TokenTime >= 0
ORDER BY CreateTime DESC
```
SQLite 上下文:
```text
CRM.Manage.DataManage.SQLiteCRMDbContext.CategoryInFoModel
```
由于 `SQLiteDbSet<T>` 使用类型名加复数后缀生成表名,逻辑表名为:
```text
CategoryInFos
```
客户模型通过以下字段关联主分类:
```text
ClientInFo.CategoryID
RecoveryClientInFo.CategoryID
```
### 9.2 CRM 客户标签
标签定义模型:
```text
CRM.Models.ClientLabelConfig
```
客户标签关系模型:
```text
CRM.Models.ClientLabelInFo
```
核心关系字段:
```text
ClientLabelInFo.ClientID
ClientLabelInFo.LabelID
```
有效标签由:
```text
ClientLabelConfigManage.GetStatusIsTrue()
```
读取,条件为:
```sql
Status = 1
AND TokenTime >= 0
ORDER BY Status, NumberValue, CreateTime DESC
```
逻辑 SQLite 表名为:
```text
ClientLabelConfigs
ClientLabelInFos
```
`ClientLabelInFo.LabelID` 再通过 `ClientLabelConfigManageModel.GetByID` 解析标签定义。
### 9.3 与实时会话的边界
CRM 的 `CategoryID``ClientID``LabelID` 不出现在已确认的实时会话分类任务中:
```text
changesidkind
changecolor
```
因此:
- `CategoryInFo` 是 CRM 客户主分类;
- `ClientLabelConfig` 是 CRM 标签定义;
- `colorkind*_share` 是商务通实时会话客户颜色分类;
- 三者不能仅凭名称互相替换。
`ContactsCategoryFrm` 使用的则是 `ContactsDS` 联系人类别数据,也不是实时会话颜色分类。
## 10. 批量/历史记录场景
历史记录页面和批量操作页面也复用了聊天分类和颜色字段:
```text
LiveWS.LR.Dialog.RecordsSqliteFrm
LiveWS.LR.Dialog.MBE_RecordsSqlite
```
批量聊天分类逻辑使用 `ModelUpdateChatKindItem` 一类的数据对象,包含:
```text
sid
cid
chatkind
```
以及渠道相关字段。
这说明批量场景仍然围绕商务通会话 `sid`、访客 `cid``chatkind` 组织,而不是围绕 CRM 的 `CategoryID`/`LabelID`。批量请求的完整聚合发送路径仍受反编译控制流混淆影响,兼容实现应先复用单会话的 `SetSidKind.aspx` 合同,再单独补充批量重试和幂等。
## 11. GoChat 对接建议
### 11.1 应实现的商务通协议
第一阶段应实现四个独立能力:
1. 站点配置读取 `sidkind_share`
2. 站点配置读取 `colorkind0_share``colorkind1_share`
3. 会话对话分类更新 `oc/SetSidKind.aspx`
4. 会话客户颜色分类更新 `oc/changecolor.aspx`
### 11.2 GoChat 内部字段建议
```text
conversation.chat_kind / custom_attributes.swt_chat_kind
conversation.custom_attributes.swt_customer_color
```
其中:
- `swt_chat_kind` 保存商务通 `chatkind`/`kind`
- `swt_customer_color` 保存商务通 `colors`/`c0`
- `c1` 可作为当前分类名称缓存,但名称应以站点分类配置为准;
- `CategoryID``LabelID` 继续留在 CRM Contact 数据模型,不写入实时会话分类字段。
### 11.3 必须保留的协议细节
- 对话分类列表使用 `|` 分隔记录、`,` 分隔字段;
- 客户颜色分类使用两个平行的 `|` 分隔数组;
- 会话分类参数名是 `kind`,不是 `chatkind`
- 客户颜色参数名是 `c0`/`c1`,不是 `CategoryID`/`LabelID`
- 服务端成功标记是 `r=ok`
- 修改会话分类时必须保留 `sid`
- 修改客户颜色时必须保留访客级 `cid/cookies`,不能用 `sid` 猜测替代;
- 两个操作都应使用持久化出站任务和幂等键,避免网络超时后重复修改。
## 12. 尚未完全确认的部分
1. PC 端任务完成后,`visitors.chatkind`/`visitors.colors` 在所有渠道上的本地刷新入口被混淆在通用事件和渠道管理器中,未确认存在统一的直接 SQLite UPDATE
2. `RESET` 在清除颜色分类时的完整表单值尚未从所有分支中还原;
3. 批量历史记录操作的最终聚合请求与失败重试策略仍需结合运行时抓包或测试账号验证;
4. CRM `CategoryInFo`/`ClientLabelConfig` 与商务通服务端是否存在额外同步接口,在 PC 端当前反编译范围内没有证据。
这些未确认项不影响四个核心接口和字段映射的结论,但实现时不能自行把 CRM 分类、标签映射到实时会话颜色分类。