坐席管理
1. 坐席状态机
注册上线
Offline ──────────────────► Idle (空闲)
▲ │
│ │ 来电振铃
│ ▼
│ Ringing
│ │
│ ┌──────┴──────┐
│ │ 接听 │ 未接
│ ▼ ▼
│ Busy (回到 Idle)
│ │
│ │ 挂断
│ ▼
│ Wrapup (话后整理)
│ │
│ 超时/手动结束
│ │
└───────────────────┘
│ 手动离开
▼
Away / Dnd / Custom
| 状态 | 说明 | 可接收呼叫 |
|---|---|---|
| Offline | 未注册/离线 | 否 |
| Idle | 空闲,等待分配 | 是 |
| Ringing | 振铃中 | 否 |
| Busy | 通话中 | 否 |
| Wrapup | 话后整理 | 否 |
| Away | 离开(如午休) | 否 |
| Dnd | 免打扰 | 否 |
| Custom | 自定义状态 | 否 |
1.1 状态转换 API
POST /cc/agents/{agent_id}/status
Body: { "status": "idle" }
可转换的状态:
idle→ 进入空闲away→ 离开dnd→ 免打扰custom:lunch→ 自定义状态(午休)offline→ 离线
1.2 话后整理(Wrapup)
通话结束后坐席自动进入 Wrapup 状态:
- 默认超时 60 秒(
wrapup_timeout_secs配置) - 坐席可手动提前结束(
POST /cc/agents/{agent_id}/wrapup/end) - 超时后自动回到 Idle
2. 创建坐席
2.1 控制台
Call Center → 坐席 → 新建:
| 字段 | 说明 | 示例 |
|---|---|---|
| 坐席 ID | 唯一标识 | agent-001 |
| 显示名 | 显示名称 | 张三 |
| 分机 | SIP 分机号 | 1001 |
| 技能组 | 所属技能组 | sales |
| 最大并发 | 同时处理通话数 | 1 |
| 优先级 | ACD 分配优先级(0-100) | 50 |
2.2 批量操作
坐席 → 批量更新:可批量修改技能组、最大并发等。
2.3 导出/重载
- 导出:将坐席配置导出为 TOML 文件
- 重载:从 TOML 文件重新加载坐席配置
3. 坐席终端
每个坐席可绑定多个终端(SIP 软电话、硬话机、WebRTC):
坐席 agent-001
├── SIP 分机 1001 (注册到 RustPBX)
├── cc-phone (Web 软电话)
└── 可同时振铃
3.1 端点注册
坐席终端通过标准 SIP REGISTER 注册到 RustPBX,系统自动关联到对应坐席。
3.2 CC Phone 认证
CC Phone 使用独立的认证机制(/cc/phone/config),支持:
- 基于控制台 Session 的认证
- 独立的坐席登录(
phone_auth) - SSO 接管(0.5+):原生客户端通过 OTC(一次性码)交换完成登录,无需内嵌凭证
3.3 外呼线路与坐席级外呼策略(0.5+)
坐席外呼不再需要单独的外呼配置,由 CC 插件统一管理:
- 坐席级外呼策略限制每个坐席可用的路由、Trunk 和外显号码——在控制台配置并支持热更新。
- 允许的线路通过
desk.lines[]下发给 CC Phone,话务台显示外呼线路选择器,每条线路的拨号前缀规则在服务端执行。 - 策略过滤在派单时执行,坐席只能通过策略允许的线路外呼,外显号码自动附加。
4. 坐席统计
每个坐席维护以下统计指标(cc_agent_stats 表):
| 指标 | 说明 |
|---|---|
| 总通话数 | 处理的通话总数 |
| 总通话时长 | 累计通话秒数 |
| 平均通话时长 | 总时长 / 总通话数 |
| 登录时长 | 当日在线总时长 |
| 空闲时长 | 当日空闲总时长 |
| 话后整理时长 | 当日 wrapup 总时长 |
| 休息时长 | 当日 away/custom 总时长 |
| 分类型休息统计(0.5+) | 按休息类型(午休/培训/…)分别累计 |
可通过 API 查询:
GET /cc/agents/{agent_id}/dashboard
GET /cc/reports/agents
5. 坐席 Dashboard
GET /cc/agents/{agent_id}/dashboard 返回坐席视角的完整信息:
- 当前状态
- 活跃通话(如有)
- 今日统计
- 技能组信息
6. 休息类型管理
在 config.toml 中自定义休息类型:
[proxy.cc.phone]
[[proxy.cc.phone.break_types]]
id = "lunch"
label_zh = "午休"
label_en = "Lunch"
[[proxy.cc.phone.break_types]]
id = "training"
label_zh = "培训"
label_en = "Training"
[[proxy.cc.phone.break_types]]
id = "meeting"
label_zh = "会议"
label_en = "Meeting"
[[proxy.cc.phone.break_types]]
id = "personal"
label_zh = "个人事务"
label_en = "Personal"