介绍
ws-sdk是前端抽出的与后端建立长链接通信的一个工具包。
特点
基于WebSocket包装器Sockette
- 断线重连
- 错误数据上报(todo)
流程
SOCKETCONFIG
格式
const configs = {
tenantId,
id,
keyValue,
keyIv,
token,
};
localStorage.SOCKETCONFIG = JSON.stringify({
...configs,
});
connect({...configs});
| 名称 | 类型 | 含义 |
|---|---|---|
| tenantId | long | 租户id |
| id | int | 用户id |
| keyValue | string | 密钥(16位字符串) |
| keyIv | string | 密钥(16位字符串) |
| token | string | 登陆后鉴权服务返回的token |
密钥(keyIv+keyValue 均16位字符串)由用户鉴权服务下发,服务端与客户端依此对通信数据进行AES加密与解密。
来源
登陆接口返回字段 admin/v1/enterprise/user/login 返回如下:
{
"code": 1,
"ts": 1692799178,
"msg": "服务接口调用成功",
"data": {
"token": "06369b511fe74990b4f774c91c9f1583",
"userInfo": {
"id": 249,
"tenantId": 1,
"name": "杜佳桧",
"account": "dujiahui@huxun",
"mobile": "18883543938",
"mainAccount": 0,
"disabled": null
},
"socketVO": {
"keyValue": "na63neqpwowb3fnt",
"keyIv": "hnpei6oj91lvtqqk",
"token": "06369b511fe74990b4f774c91c9f1583"
}
}
}
数据协议
数据结构
请求和返回都是二进制数据。数据包含两部分:头部数据+业务二进制数据。
{
actionType: 7, // 通信的动作类型
bizType: 0, //
mid: 0, // 消息id 唯一id
status: 1, // 状态 1为成功 其他为失败场景
// 业务数据
payload: "{\"actionType\":1,\"bizType\":1,\"content\":\"你可以选择\\\"立即下载\\\"或稍后在下载中心查看\",\"ext\":\"{\\\"name\\\":\\\"客户账单报表_20230823.zip\\\",\\\"path\\\":\\\"convoy/export/20230823/5364ca4c31384bc09bf1d7319414ea4b.xlsx\\\"}\",\"showTime\":5,\"title\":\"导出文件准备完成\"}",
timestamp: 1692779497702 // 时间
}
字段含义:
| 名称 | 类型 | 含义 |
|---|---|---|
| actionType | int | 通信的动作类型 |
| bizType | int | 业务类型(见数据类) |
| mid | long | 消息id 唯一id |
| status | int | 状态 1为成功 其他为失败场景(具体见数据类) |
| timestamp | long | 当前时间戳毫秒 |
| payload | string | 业务数据 |
actionType
| 值 | 含义 |
|---|---|
| 1 | 建立连接(CONNECT) |
| 2 | 上行业务 (UP) |
| 3 | 下行业务 (DOWN) |
| 5 | 回执 (ACK) |
| 6 | 心跳 (PING) |
| 7 | 心跳响应 (PONG) |
| 8 | 断开连接 (DISCONNECT) |
payload
{
actionType: 1 // 消息展示样式
bizType: 1 //业务类型
content: "你可以选择\"立即下载\"或稍后在下载中心查看"
ext: "{\"name\":\"客户账单报表_20230823.zip\",\"path\":\"convoy/export/20230823/5364ca4c31384bc09bf1d7319414ea4b.xlsx\"}"
showTime: 5
title: "导出文件准备完成"
id: 80800
}
| 名称 | 类型 | 含义 |
|---|---|---|
| actionType | int | 消息展示样式(具体见3.3.1) |
| bizType | int | 业务类型(具体见3.3.2) |
| showTime | int | 气泡展示时长(0-常驻;3-停留3秒; |
| 当actionType为0或者1时必填) | ||
| title | string/reactNode | 卡片标题 |
| content | string/reactNode | 卡片内容 |
actionType
| 值 | 含义 |
|---|---|
| 0 | 全部执行 |
| 1 | 只推送气泡 |
| 2 | 只保存到消息盒子 |
bizType
货代 1xx 车队2xx 每个业务组自己占用一段
| 值 | 含义 | 归属业务 |
|---|---|---|
| 1 | 下载类 | 基础公共 |
| 2 | 港区数据变更 | 基础公共 |
| 100 | 开截港变更 | 货代 |
| 101 | 进港异常 | 货代 |
| 102 | 码放异常 | 货代 |
| 103 | 车队接单 | 货代 |
| 104 | 确认费用 | 货代 |
| 105 | 车队拒单 | 货代 |
| 106 | 到厂预警 | 货代 |
| 107 | 运抵预警 | 货代 |
| 200 | 货代生成派车单 | 车队 |
| 201 | 货代退关 | 车队 |
| 202 | 预录异常 | 车队 |
| 203 | 箱货对比异常 | 车队 |
| 204 | 开截港变更 | 车队 |
| 205 | 货代确认费用 | 车队 |
| 206 | 货代变更费用 | 车队 |
ext
{
name: '客户账单报表_20230823.zip',
path: 'convoy/export/20230823/5364ca4c31384bc09bf1d7319414ea4b.xlsx'
}
业务数据
业务数据是需要经过序列化、压缩和加密的二进制数据
流程图如下:
在发送数据之前需要将业务对象实体经过序列化、压缩和加密成二进制数据,在读取则反之。
- 序列化协议:json
- 经过json序列化并以UTF-8编码转成二进制字节流。
- 压缩:zlib
- 标准zlib压缩算法。
- 加解密:AES/CBC/PKCS7Padding (128位)
- 密钥(keyIv+keyValue 均16位字符串)由用户鉴权服务下发,服务端与客户端依此对通信数据进行AES加密与解密
接口
建立连接
头部动作类型:1(CONNECT) 解释: 用户在登陆成功后,带着认证token信息,申请与websocket服务端建立连接,在建连成功后即可传输数据。 注意:这与上面的数据读写流程并不一致(传输的二进制数据只有序列化,没有压缩/加密),因为是初次连接,需要拿到token等信息,但里面的body字段仍然是序列化&压缩&加密的二进制字节数组。
入参定义:
| 名称 | 类型 | 含义 |
|---|---|---|
| token | string | 登陆后鉴权服务返回的token |
| terminal | int | 设备类型 |
| body | byte[] | 消息体经过序列化并已UTF-8转为字节数组、压缩、加密 |
消息体:
| 名称 | 类型 | 含义 |
|---|---|---|
| timestamp | long | 当前时间戳毫秒 |
| deviceToken | int | 设备信息,要求非空,可取mac地址 |
| os | int | 端上操作系统类型,0:android,1:ios,2:windows |
| keepAlive | int | 默认空闲时长 默认15s |
| appVersion | string | 客户端版本,要求非空 |
ACK回执
头部动作类型:5(ACK) 解释: 客户端火服务端在收到对方的消息并处理后,都会给对方返回消息处理结果,即收到ack代表消息已被接收处理。
- 比如客户端在请求建连后,服务端可能会回复"成功"(status=1),也可能会回复"鉴权失败"(status=1505)。
- 客户端在收到服务端push的消息后,也会给服务端回复"成功"(status=1),同时带上确认的mid。
入参定义:
| 名称 | 类型 | 含义 |
|---|---|---|
| timestamp | long | 当前时间戳毫秒 |
| mid | long | 消息id 唯一id 不可重复 用于确认当前消息已接收 |
| bizType | int | 业务类型 |
| status | int | 状态 1为成功 其他为失败场景 |
注意: 前端能处理的最大数为16位,切记不能超过16位,超过会出现精度丢失,导致mid匹配不上。 mid精度丢失的问题,原因是mid是个大数,js的number类型有个最大值(安全值)。即2的53次方减1,为9007199254740991。如果超过这个值,那么js会出现不精确的问题。这个值为16位。现在我们的mid是18位。
心跳机制PING/PONG
头部动作类型:6(PING)或7(PONG) 解释: 为了保持连接通道活跃,客户端每隔一段时间向服务端发送PING数据,服务端则回PONG数据 默认心跳间隔时间: 15s 入参定义:
| 名称 | 类型 | 含义 |
|---|---|---|
| timestamp | long | 当前时间戳毫秒 |
| mid | long | 消息id 唯一id 不可重复 用于确认当前消息已接收 |
客户端主动断连
头部动作类型:8(DISCONNECT)。 解释: 客户端或者服务端向另外一端发起关闭连接请求(比如在用户在页面退出登陆时或者多端登陆踢登) 入参定义:
| 名称 | 类型 | 含义 |
|---|---|---|
| timestamp | long | 当前时间戳毫秒 |
| mid | long | 消息id 唯一id 不可重复 用于确认当前消息已接收 |
数据上行
头部动作类型:2(UP)。 解释: 客户端向服务器发送数据,即为“上行”。 其中payload存放业务特定的消息对象,由业务自定义,通常为json格式。
入参定义:
| 名称 | 类型 | 含义 |
|---|---|---|
| timestamp | long | 当前时间戳毫秒 |
| mid | long | 消息id 唯一id 不可重复 用于确认当前消息已接收 |
| bizType | int | 业务类型 |
| payload | string | 消息体 业务自定义数据的json格式 |
数据下行
头部动作类型:3(DOWN)。 解释: 服务端向客户端发送数据,即为“下行”。 其中payload存放业务特定的消息对象,由业务方自定义,通常为json格式。
入参定义:
| 名称 | 类型 | 含义 |
|---|---|---|
| timestamp | long | 当前时间戳毫秒 |
| mid | long | 消息id 唯一id 不可重复 用于确认当前消息已接收 |
| bizType | int | 业务类型 |
| payload | string | 消息体 业务自定义数据的json格式 |
数据类
terminal
public enum TerminalEnum {
ENTERPRISE(0, "企业中心"),
AGENT(1, "货代"),
CONVOY(2, "车队"),
YARD(3, "堆场"),
WAREHOUSE(4, "仓库"),
SHIPPING_COMPANY(5, "船公司"),
SHIP_AGENCY(6, "船代"),
WHARF(7, "码头"),
BOX_AGENT(8, "放箱代理"),
LEASING_COMPANY(9, "租箱公司"),
MOBILE(20,"司机端"),
;
}
bizType
各自业务占用1段code码,不要直接累加
public enum BizTypeEnum {
UNKNOWN(0, "unknown"),
/**
* 手机状态 上行
*/
APP_STATUS(1, "app_status"),
/**
* 系统消息 下行
*/
SYSTEM(10, "system"),
/**
* 订单创建 下行
*/
ORDER_CREATE(100, "order_create"),
;
}
status
public enum SocketStatusEnum {
SUCCESS(1, "success"),
PARAM_ERROR(1501, "illegal param"),
CONNECT_TIMEOUT(1502, "connect timeout"),
CONNECT_SERVER_UNAVALILABLE(1503, "server unavailable"),
CONNECT_BODY_NULL(1504, "body is null"),
AUTH_FAILED(1505, "auto failed"),
CHANNEL_NOT_EXISTS(1506, "Channel not be found."),
ILLEGAL_TYPE(1507, "Illegal type."),
TOO_MANY_REQUEST(1508, "Too many request."),
DECRY_DATA_FAILED(1509, "Decrypt data failed."),
DECOMPRESS_DATA_FAILED(1510, "Decompress data failed."),
MULTI_TERMINAL_FAILED(1511, "Push multi-terminal failed."),
MULTI_TERMINAL_NOT_SURE(1512, "Push multi-terminal success or failed, not sure."),
RATELIMIT_PUSHMESSAGE(1513, "Rate limit push socket message."),
ACM_CONFIG_CANNOT_SEND(1514, "BizType not in ACM config,cannot push socket message."),
BATCHPUSHMESSAGE_SOME_FAILED(1515, "Batch push message,but some failed.");
}