worker-ws-task-push.md 9.6 KB

工作机 APP WebSocket 任务推送对接说明

1. 概述

销售 PC 端在「启动外呼任务」时,若任务为 APP 外呼模式callModel = 3 / 4 / 5)且已绑定设备,服务端会通过 同一条 WebSocket 长连接 向对应工作机推送 taskRun 消息。

  • 心跳任务推送共用连接地址,通过消息体字段 cmd 区分类型。
  • APP 收到 taskRun 后,根据 callModel 跳转或刷新对应外呼页面。
服务 端口 说明
fs-worker-app 8008 WebSocket + APP HTTP 接口
fs-saas-company 8006 PC 端启动任务(taskRun

2. WebSocket 连接

2.1 连接地址(唯一)

ws://{host}:8008/worker/webSocket/{meId}?tenantId={tenantId}
参数 说明
meId 设备 IMEI,路径参数;须与登录 meId、主库 company_sms_device.imei 一致
tenantId 租户 ID,查询参数,建议必传

示例:

ws://192.168.11.62:8008/worker/webSocket/8601234561212129012?tenantId=338

2.2 连接生命周期

  1. APP 登录成功后建立长连接(与心跳共用)。
  2. 连接成功时,服务端主动推送一次当前设备状态。
  3. APP 定时发送心跳(建议间隔 30s)。
  4. PC 启动任务时,服务端可能额外推送 taskRun(无需 APP 回包)。

3. 消息协议(统一 JSON)

所有消息均为 JSON 文本,通过字段 cmd 区分类型。

3.1 消息类型一览

方向 cmd 说明
APP → 服务端 heartbeat 心跳
APP → 服务端 deviceStatus 主动查询设备状态(可选)
服务端 → APP heartbeat 心跳回复
服务端 → APP deviceStatus 禁用/离线等状态变更
服务端 → APP taskRun PC 启动任务推送
服务端 → APP error 处理异常

4. 心跳(已有机制,与任务推送无关)

4.1 APP 发送

{
  "cmd": "heartbeat",
  "tenantId": 338,
  "appVersion": "1.0.0",
  "deviceName": "工作机A",
  "phone1": "13800138000",
  "phone2": "13900139000",
  "imei": "卡1IMEI,卡2IMEI"
}

4.2 服务端回复

{
  "cmd": "heartbeat",
  "meId": "8601234561212129012",
  "status": 1,
  "msg": "ok",
  "appVersion": "1.0.0",
  "deviceName": "工作机A",
  "timestamp": 1718534400000
}

status0 离线 / 1 在线 / 2 禁用。

4.3 APP 处理建议

  • 更新本地设备状态展示。
  • status === 2 时提示禁用并限制外呼。
  • 心跳逻辑保持现有实现,无需为 taskRun 单独建连接

5. 任务推送 taskRun(本次对接重点)

5.1 触发条件(PC 端)

PC 调用 GET /company/companyVoiceRobotic/taskRun?id={任务ID} 且满足:

  • 任务 callModel345
  • 任务 deviceIds 非空(已绑定设备);
  • 设备 IMEI 能解析成功。

不满足上述条件时 不会推送 taskRun

5.2 推送链路

PC taskRun (8006)
  → Redis PUBLISH channel: worker:ws:task:run
  → fs-worker-app Redisson 订阅
  → WorkerWebSocketServer.pushTaskRunToDevices
  → 向在线设备的 WebSocket Session 发送消息

前置条件:8006 与 8008 使用同一 Redis 实例;APP 已连接 WS 且 meId 在推送列表中。

5.3 服务端 → APP 消息体

{
  "cmd": "taskRun",
  "robotic": 294,
  "callModel": 3,
  "timestamp": 1718534400000
}
字段 类型 说明
cmd String 固定 taskRun
robotic Long 外呼任务 ID(company_voice_robotic.id
callModel Integer 外呼模式:3 / 4 / 5
timestamp Long 服务端时间戳(毫秒)

说明taskRun 为服务端单向推送,APP 不需要回包


6. callModel 与页面对应关系

callModel 名称 业务含义 APP 页面/行为建议
3 APP-网页点呼 销售在 APP 上手动点选客户逐个拨打 跳转或刷新 网页点呼页;展示该 robotic 任务下的待拨客户列表,用户手动点击拨打
4 APP-自动呼叫 APP 按任务自动连续外呼 跳转或刷新 自动呼叫页;进入自动拨号流程,按队列自动拨打
5 APP-AI呼叫 APP 侧 AI 辅助 / AI 外呼流程 跳转或刷新 AI 呼叫页;进入 AI 外呼交互流程

后端枚举定义见 AiCallModeEnum

  • 3 = APP_WEB(APP-网页点呼)
  • 4 = APP_AUTO(APP-自动呼叫)
  • 5 = APP_AI(APP-AI呼叫)

注意callModel1(人工外呼)、2(AI 自动外呼 / EasyCall)时 不会 收到 taskRun 推送。

6.1 收到 taskRun 后的推荐处理流程

onWebSocketMessage(json)
  → 解析 cmd
  → if cmd == "taskRun":
       roboticId = json.robotic
       callModel   = json.callModel
       switch (callModel):
         3 → 打开/刷新「网页点呼」页,带 roboticId,拉取任务与客户列表
         4 → 打开/刷新「自动呼叫」页,带 roboticId,启动自动拨号
         5 → 打开/刷新「AI 呼叫」页,带 roboticId,进入 AI 外呼流程

若当前已在对应模式页面,仅需 刷新任务列表/客户列表(调用下方 HTTP 接口),不必重复跳转。


7. 配套 HTTP 接口(刷新页面数据)

收到 taskRun 后,按 callModel 调用对应接口拉取数据(均需登录态 WorkerToken)。

7.1 执行中任务统计列表

GET /company/companyVoiceRobotic/myTaskStatList?callModel={3|4|5}&meId={meId}
  • callModel:与推送消息一致(3 / 4 / 5)
  • meId:当前设备 IMEI

用于各外呼模式首页的任务列表刷新。

7.2 任务拨打统计 / 客户列表

GET /company/companyVoiceRoboticCallLogCallphone/...?roboticId={robotic}&callModel={3|4|5}&meId={meId}

(具体路径见 CompanyVoiceRoboticCallLogCallphoneController,参数需与 callModelmeId 一致。)

7.3 拨打前校验

POST /app/aiSipCall/validateAppCall

Body 示例:

{
  "roboticId": 294,
  "calleeId": 12345,
  "meId": "8601234561212129012",
  "callModel": 3
}

7.4 外呼结果回调

POST /app/aiSipCall/appCardCallBack

通话结束后回调,Body 中需带 callModel(默认 3)。

7.5 暂停/继续任务

POST /company/companyVoiceRobotic/pauseRoboticActive

与 PC 端共用,APP / PC 任意一端操作会联动。


8. APP 端集成示例(伪代码)

// 同一 WebSocket 连接
const ws = new WebSocket(
  `ws://${host}:8008/worker/webSocket/${meId}?tenantId=${tenantId}`
);

ws.onopen = () => {
  startHeartbeat(); // 定时发 cmd=heartbeat
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  switch (msg.cmd) {
    case 'heartbeat':
      handleHeartbeatReply(msg);      // 更新在线/禁用状态
      break;
    case 'deviceStatus':
      handleDeviceStatus(msg);        // 禁用、离线提示
      break;
    case 'taskRun':
      handleTaskRunPush(msg);         // 任务推送(见下)
      break;
    case 'error':
      showError(msg.msg);
      break;
  }
};

function handleTaskRunPush(msg) {
  const { robotic, callModel } = msg;
  if (callModel === 3) {
    navigateOrRefresh('WebClickCallPage', { roboticId: robotic, callModel: 3 });
  } else if (callModel === 4) {
    navigateOrRefresh('AutoCallPage', { roboticId: robotic, callModel: 4 });
  } else if (callModel === 5) {
    navigateOrRefresh('AiCallPage', { roboticId: robotic, callModel: 5 });
  }
  fetchMyTaskStatList(callModel, meId);
}

function startHeartbeat() {
  setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({
        cmd: 'heartbeat',
        tenantId,
        appVersion,
        deviceName,
        phone1,
        phone2,
        imei: cardImeiJoined
      }));
    }
  }, 30000);
}

9. 联调检查清单

步骤 期望现象
APP 连接 WS 服务端日志:[WorkerWS] 设备连接: meId=...
8008 启动 日志:[WorkerTaskRunWS] Redisson 订阅成功 channel=worker:ws:task:run
PC 点「启动任务」 8006:[WorkerTaskRunWS] 已发布 roboticId=... callModel=... meIds=[...]
8008 收 Redis [WorkerTaskRunWS] 收到消息 roboticId=...
设备在线 [WorkerWS] 任务启动推送成功 meId=...
APP 收消息 cmd=taskRun,按 callModel 进入对应页面

常见问题

  1. 收不到 taskRun:检查 meId 是否与 deviceIds 解析出的 IMEI 一致;APP 是否已连 WS。
  2. 8008 收到消息但 APP 没反应:检查 APP onMessage 是否处理 cmd === 'taskRun'
  3. 推送成功但页面无数据:检查刷新接口的 callModelmeId 是否与推送一致。

10. 附录:完整消息字段(WorkerWsMsg)

字段 类型 说明
cmd String heartbeat / deviceStatus / taskRun / error
meId String 设备 IMEI
tenantId Long 租户 ID
appVersion String APP 版本
deviceName String 设备名称
phone1 / phone2 String 双卡手机号
imei String SIM 卡 IMEI(逗号分隔)
status Integer 0 离线 / 1 在线 / 2 禁用
msg String 提示信息
timestamp Long 时间戳
robotic Long 任务 ID(仅 taskRun
callModel Integer 外呼模式(仅 taskRun

文档版本:与 fs-worker-app WebSocket + Redis 任务推送实现同步。 Redis 通道worker:ws:task:runWorkerWsConstants.REDIS_CHANNEL_TASK_RUN)。