1. 接口说明

提供两类接口:

  • OPENAPI
  • 基于 WebSocket 提供实时事件通知的事件中心

2. 对接方式

2.1. OPENAPI

2.1.1. URL地址(POST方式,JSON编码)

https://api.xiaoduoai.com/gate/open

2.1.2. 公共参数

调用任何一个API都必须传入的参数,目前支持的公共参数有:

参数名称

参数类型

是否必须

参数描述

示例

method

string

具体API接口名称

xd.gate.demo

appkey

string

配给企业的AppKey

key123456

ts

int

10位的秒级时间戳

1668654293

sig

string

参数签名结果,签名算法参照下面的介绍

body

map[string]interface{}

业务参数

示例:

curl -H "content-type: application/json" https://api.xiaoduoai.com/gate/open -d '
{
    "method": "'${method}'",
    "body": {
        "key": "value"
    },
    "appkey": "key123456",
    "ts": '${ts}',
    "sig": "'${sig}'"
}'

2.1.3. 签名

为了防止API调用过程中被篡改,调用任何一个API都需要携带签名,服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。

签名方式:将methodtsapp_secret拼接得到的字符串MD5取16进制小写32位字符串

示例:

参数method=xd.gate.demots=1668654293app_secret=secret456

拼接后字符串 xd.gate.demo1668654293secret456

最终得到的签名为:159235348d54ea0c0d42bc8c849f75ed

2.1.4. demo.sh

#!/bin/bash

method="xd.gate.demo"
ts=`date +%s`
sig=`echo -n "xd.gate.demo1668654293secret456"|md5sum|cut -d ' ' -f1`

curl -H "content-type: application/json" https://api.xiaoduoai.com/gate/open -d '
{
    "method": "xd.gate.demo",
    "body": {
        "body_key": "body_val"
    },
    "appkey": "key123",
    "ts": 1668664279,
    "sig": "b09914f8721c16617994e0ac9c9c45fc"
}'

2.1.5. 接口调用频率限制

调用API接口每分钟允许请求的次数,限制说明详情如下

API接口

接口名称

并发量 & 频率/分钟

xd.ticket.create

创建工单

1 & 60次/分钟

xd.ticket.update

更新工单

1 & 60次/分钟

xd.ticket.list

查询工单

1 & 60次/分钟

xd.ticket.batch_pay_list

查询批量打款工单

1 & 60次/分钟


2.2. 使用WebSocket接入

2.2.1. 连接到Gateway

ws://api.xiaoduoai.com/event/open

2.2.2. 鉴权连接

建立 websocket 连接之后,需要进行鉴权

参数名称

参数类型

是否必须

参数描述

示例

sub_event_ids

[]string

订阅消息的接口名称

xd.gate.demo

app_key

string

配给企业的AppKey

key123456

ts

int

10位的秒级时间戳

1668654293

sig

string

参数签名结果,签名算法参照下面的介绍

示例:

ws.onopen = function(evt)
        {
            const ts = parseInt(Date.now() / 1000)
            const auth = {
                "sub_event_ids" : ["xd.event.demo"],
                "appkey": "key123",
                "ts": ts
            };
            auth.sig = md5("xd.event.demo"+ts+"secret456")
            const data = JSON.stringify(auth);
            ws.send(data)
        };

2.2.3. 签名

签名方式:将sub_event_ids(用,连接)、tsapp_secret拼接得到的字符串MD5取16进制小写32位字符串

示例:

参数 sub_event_ids=["xd.gate.demo","xd.gate.demo2"]ts=1668654293app_secret=secret456

拼接后字符串xd.gate.demo,xd.gate.demo21668654293secret456

最终得到的md5签名为:b0ce12faf07c250aa799a243ad4a9de5

2.2.4. ws_client.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>打开F12开发者工具查看浏览器控制台标准输出</title>
  </head>
  <body>

  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js"></script>
  <script type="text/javascript">
    window.onload = function(){
      // 后端针对每个appkey仅支持一个并发接入,
      // 后接入的会踢掉已建立连接的

      const ws = new WebSocket("ws://api.xiaoduoai.com/event/open");
      // 第一个包,连接鉴权
      ws.onopen = function(evt)
        {
          const ts = parseInt(Date.now() / 1000)
          const auth = {
            "sub_event_ids" : ["xd.event.demo"],
            "appkey": "key123",
            "ts": ts
          };
          auth.sig = md5("xd.event.demo"+ts+"secret456")
          const data = JSON.stringify(auth);
          ws.send(data)
          window.setInterval(function () {
            sendPing(ws);
          }, 20000);
        };

      // 发送心跳,后端会立即返回event_id = ping的数据
      const sendPing = function(ws)
        {
          const ping = {
            "event_id" : "ping"
          }
          const data = JSON.stringify(ping);
          ws.send(data)
        }

      ws.onmessage = function(evt)
        {
          console.log("message " + evt.data)
          const ret = JSON.parse(evt.data)
          console.log("message " + ret["event_id"])
          if (ret["event_id"] == "ping") {

          } else {
            console.log("message " + ret["data"].message)
          }
        };

      ws.onclose = function(evt)
        {
          // 关闭 websocket
          console.log("closed " + evt)
        };

      ws.onerror = function(evt)
        {
          // 异常退出,需要处理断线重连
          console.log("error " + evt)
        };
    }
  </script>
</html>

注意事项

  1. 连接1min内没有发消息会自动断开
  2. 同一个appkey只能保持一个连接,多个连接只会保留最后一次连接

2.3. 回调地址

  1. 回调地址是用来接收工单变更事件,工单字段变更后,会在回调地址中接收到加密之后的变更消息。
  2. POST方式请求,将相应的消息存放在请求体中,使用企业的app_secret直接对请求体进行AES解密即可获取对应的信息。
  3. 若回调失败,即第三方响应code不为0,那么会每隔5s进行重试,最多重试3次。
    1. 3次都没有成功响应,则会在对应工单中记录一条推送失败的操作记录。
    2. 若多次推送都失败,第三方可通过api接口xd.ticket.list拉取工单数据。

2.3.1. 回调地址要求

  • 推送的消息是POST方式,编码格式为utf-8
  • 不支持传自定义参数

2.3.2. 消息解析

通过解析消息体,可以获取每条消息的具体信息

(1)推送给第三方的加密消息

curl --location '{{host}}' 
--header 'Content-Type: application/json' 
--data '{
    "version": "v0.0.1",
    "data": "jF5gtaRtxPX1XqOhuN+uFs+FgkYfTLBUh29OrghmTEAfVMqXGYAO4k1mbKuzhZnkQuAE8sAfhTIrqrgGMycDeJ00Bbx8H2rQxJdpCQGlK5wHhIo20takjXy0VIqQ+wy/sYMjwcPDVVcyB4tdb5S4d7CroP00o0mREloX4kmq4N3bum2dl4+X5iKV0aRTlYWcVFsre7PcF7OF/oBTQ14eF2yYILTTopTWHsPBUId0jARGhFIPnBLiv6a6gqMmouIfq4I4ckHCugmEduZP0sF5ITFIkqgL/ttDK/dcdY8UN1MgrUQrteT/1juz/CV35o/8Y9GeZRWCJNqXJaX5rBgXUM4Dx0a+IcRLcxydlegmi3uzxKacEUw9X76q/S7ePdbzJe/6ma5HTLl8zi+6uuunmZMUUg8Oo70yKuomVJylgCXiX3RgBmbDLXT6q/osfcyueSiHVqcOpw23+qSZ1ibkjC4ln1ouJjBQVBpRKLcTKkszBUTXLwRsHXTzYQIn8ZUMruJTiiVPw9rebCAKctUZZmPIRurikao6h4gHLLlYMDErq1KhCOf4WIpiB1jTXTfusJG1bdUBSTmQoFguwdVc90BENFGKI6iVADBlgVUyRhdscd2d6fp2+MDSdMaeZR/bt1o+gvqw7hrCST6Cj+ndKREDRSbV5yNPau7YAjcK8ikQyMVBd1OjXDCwuy/0dImcNpdzy3ZkI9BFc5lA4OC/w7POsBpwQaqRrCIqBJM4Phir7KcatSua51lHhLTrPuwXC7i/YLzLAkUhDqpRMkjUiej7Jf6MXdAGhnmqgpxOjS/JcvKqbObR5qNlEXSKrZzbkIOGXxZcADBn60AXqZ+Gn+izsH+mGce3tSbWgGTeIDRdWcD57kHIh/JNSalU5MckoWoOdXZYvUuHzpjgX765+BrPZ2SIewWl5MZobUHEVyGkqpHSBXmRlyYQROrl5a0G"
}'

(2)app_secret

nkwpdVyagzTcFXJt

(3)解密之后的消息

{
    "event_id": "xd.ticket.result",
    "event_time": 1670240307806,
    "version": "v0.0.1",
    "data": {
        "version": "v0.0.1",
        "category": "仓库",
        "cf_values": [{
            "name": "优先级",
            "value": "普通",
            "type": "优先级",
            "is_changed": true
        }, {
            "name": "处理人",
            "value": "待分配",
            "type": "处理人"
        }, {
            "name": "补发原因",
            "value": "破损",
            "type": "下拉"
        }],
        "create_time": 1670240307000,
        "creator_name": "杜可风按",
        "enterprise_id": "q-610a49e0db1d8875d8980e41",
        "source": "第三方",
        "ticket_entry_id": 1,
        "ticket_seq": 1,
        "ticket_status": "已完成",
        "ticket_type": "换货",
        "ticket_type_id": 1,
        "update_time": 1670240307000,
        "message_type": "ticket_update",
        "comments": [{
            "content": "备注内容",
            "create_time": 1670240307000,
            "account_name": "张三"
        }]
    }
}

(4)第三方返回的响应(无需加密):

若失败,需返回message错误原因

{
  "code": 0,
  "message": ""
}

2.3.3. 解密算法

Go解密代码:

func main() {
    // 回调地址接受到的加密内容
    msgSecret := "jF5gtaRtxPX1XqOhuN+uFs+FgkYfTLBUh29OrghmTEAfVMqXGYAO4k1mbKuzhZnksf5BPW2/8jmi1zIYwUG9jtZTWbLgwZB57EowtXP5H8qfsu4gBClufVT9cebZHYyFtYZ64btsIF8EUtsU9+p5bvNNB/k7urXfR+e7hk8XOMhfZI70ZRa+BEoNY8Br6kkcfZdV4S6mimViJJkhLBkbbCN22ZKDx/swzp8wGmI9y9RLLtjGPzUv6DJ+xyKu8fYTQjeYXHu9T/fGOhT6nbXbod0KBv8zODT9glbAXQg5ZSEKb8H2wSTBMy2SzswAksUPfRPnhMtSMbL164gybAd7TAXA7F+HLYxfvN2gPd4yDTlH1s2/CYGVLXAMoZVxR+hBY8QHn+f+sxLiAU1TstuRscT1AxIh5UY8KGKIrZi86qEa657JEPGFS2m1i4s5/eZwHxTj2D79l5uV0ek6vmqAumz/VLC0Uf7tpK96pAwdkA8ttifxkn3aknfzPgZgsp56czqk4sci88GHCQwv4IQn5zqA/GrmmmHNLxjsQ4tojMavrGMjrOI28k+s8H1YUWwvoBH6J5cyMi9Yb9JdL7/BKz9mtv4zpr7rQBuVe+rVyMHNa+bZ/eL2HCbqoj7OT8FGogPZQntmTfoXjfPn4AscUQrAIwpT5Z6edo19Ae4qj3kjGJ/Q0JwoTjTslIeAfRc9LRCjECNpHLTzHICi7NDDLrVE2L+OT6FeRrbYwpNGFQvcKNtL5ad3dXXj17jIob9SaijRKsDJZBHfIgxOGjHNhA20NPF/mRfxdU1erqX1N3gZsOEtPIui0yQt1X+Q02hD" //加密之后的消息
    // 企业的 app_secret
    appSecret := "nkwpdVyagzTcFXJt"
    // 获取解密后的内容
    sourceMsg := AesDecrypt(msgSecret, appSecret)
    fmt.Printf("解密后:%sn", sourceMsg)
}


func AesDecrypt(msgSecret, appSecret string) string { 
    bytesPass, err := base64.StdEncoding.DecodeString(msgSecret)
    if err != nil {
        fmt.Println(err)
        return "解密失败!!!"
    }
    sourceMsg, err := DoAesDecrypt(bytesPass, []byte(appSecret))
    if err != nil {
        fmt.Println(err)
        return "解密失败!!!"
    }
    return string(sourceMsg)
}

func DoAesDecrypt(encryptedMsg, key []byte) ([]byte, error) { 
    block, err := aes.NewCipher(key) 
    if err != nil { 
        return nil, err 
    } 
    //AES分组长度为128位,所以blockSize=16,单位字节 
    blockSize := block.BlockSize() 
    blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节 
    origData := make([]byte, len(encryptedMsg)) 
    blockMode.CryptBlocks(origData, encryptedMsg) 
    origData = PKCS5UnPadding(origData) 
    return origData, nil 
} 

//去除填充数据 
func PKCS5UnPadding(origData []byte) []byte { 
    length := len(origData) 
    unfilledNum := int(origData[length-1]) 
    return origData[:(length - unfilledNum)] 
}

Python解密代码:

import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def aes_decrypt(msg_secret, app_secret):
    key = app_secret.encode('utf-8')
    encrypted_msg = base64.standard_b64decode(msg_secret)
    cipher = AES.new(key, AES.MODE_CBC, key[:16])
    decrypted_data = cipher.decrypt(encrypted_msg)
    unpadded_data = unpad(decrypted_data, 16)
    return unpadded_data.decode('utf-8')
msg_secret = "jF5gtaRtxPX1XqOhuN+uFs+FgkYfTLBUh29OrghmTEAfVMqXGYAO4k1mbKuzhZnksf5BPW2/8jmi1zIYwUG9jtZTWbLgwZB57EowtXP5H8qfsu4gBClufVT9cebZHYyFtYZ64btsIF8EUtsU9+p5bvNNB/k7urXfR+e7hk8XOMhfZI70ZRa+BEoNY8Br6kkcfZdV4S6mimViJJkhLBkbbCN22ZKDx/swzp8wGmI9y9RLLtjGPzUv6DJ+xyKu8fYTQjeYXHu9T/fGOhT6nbXbod0KBv8zODT9glbAXQg5ZSEKb8H2wSTBMy2SzswAksUPfRPnhMtSMbL164gybAd7TAXA7F+HLYxfvN2gPd4yDTlH1s2/CYGVLXAMoZVxR+hBY8QHn+f+sxLiAU1TstuRscT1AxIh5UY8KGKIrZi86qEa657JEPGFS2m1i4s5/eZwHxTj2D79l5uV0ek6vmqAumz/VLC0Uf7tpK96pAwdkA8ttifxkn3aknfzPgZgsp56czqk4sci88GHCQwv4IQn5zqA/GrmmmHNLxjsQ4tojMavrGMjrOI28k+s8H1YUWwvoBH6J5cyMi9Yb9JdL7/BKz9mtv4zpr7rQBuVe+rVyMHNa+bZ/eL2HCbqoj7OT8FGogPZQntmTfoXjfPn4AscUQrAIwpT5Z6edo19Ae4qj3kjGJ/Q0JwoTjTslIeAfRc9LRCjECNpHLTzHICi7NDDLrVE2L+OT6FeRrbYwpNGFQvcKNtL5ad3dXXj17jIob9SaijRKsDJZBHfIgxOGjHNhA20NPF/mRfxdU1erqX1N3gZsOEtPIui0yQt1X+Q02hD"
app_secret = "nkwpdVyagzTcFXJt"
decrypted_msg = aes_decrypt(msg_secret, app_secret)
print(f"解密后: {decrypted_msg}")
#请注意,Python 中使用了 pycryptodome 库来提供 AES 加密和解密的功能。如果您尚未安装该库,可以通过运行 pip install pycryptodome 命令进行安装。

Java解密代码

public class Main {
    public static void main(String[] args) {
        try {
            String msgSecret = "jF5gtaRtxPX1XqOhuN+uFs+FgkYfTLBUh29OrghmTEAfVMqXGYAO4k1mbKuzhZnksf5BPW2/8jmi1zIYwUG9jtZTWbLgwZB57EowtXP5H8qfsu4gBClufVT9cebZHYyFtYZ64btsIF8EUtsU9+p5bvNNB/k7urXfR+e7hk8XOMhfZI70ZRa+BEoNY8Br6kkcfZdV4S6mimViJJkhLBkbbCN22ZKDx/swzp8wGmI9y9RLLtjGPzUv6DJ+xyKu8fYTQjeYXHu9T/fGOhT6nbXbod0KBv8zODT9glbAXQg5ZSEKb8H2wSTBMy2SzswAksUPfRPnhMtSMbL164gybAd7TAXA7F+HLYxfvN2gPd4yDTlH1s2/CYGVLXAMoZVxR+hBY8QHn+f+sxLiAU1TstuRscT1AxIh5UY8KGKIrZi86qEa657JEPGFS2m1i4s5/eZwHxTj2D79l5uV0ek6vmqAumz/VLC0Uf7tpK96pAwdkA8ttifxkn3aknfzPgZgsp56czqk4sci88GHCQwv4IQn5zqA/GrmmmHNLxjsQ4tojMavrGMjrOI28k+s8H1YUWwvoBH6J5cyMi9Yb9JdL7/BKz9mtv4zpr7rQBuVe+rVyMHNa+bZ/eL2HCbqoj7OT8FGogPZQntmTfoXjfPn4AscUQrAIwpT5Z6edo19Ae4qj3kjGJ/Q0JwoTjTslIeAfRc9LRCjECNpHLTzHICi7NDDLrVE2L+OT6FeRrbYwpNGFQvcKNtL5ad3dXXj17jIob9SaijRKsDJZBHfIgxOGjHNhA20NPF/mRfxdU1erqX1N3gZsOEtPIui0yQt1X+Q02hD";
            String appSecret = "nkwpdVyagzTcFXJt";
            String decrypt = decrypt(msgSecret, appSecret);
            System.out.println("解密后: " + decrypt);
        } catch (Exception e) {
            System.out.println("解密失败!!!");
            e.printStackTrace();
        }
    }

    public static String decrypt(String sSrc, String sKey) throws Exception {
        try {
            byte[] raw = sKey.getBytes(StandardCharsets.US_ASCII);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            int blockSize = cipher.getBlockSize();
            IvParameterSpec iv = new IvParameterSpec(sKey.substring(0, blockSize).getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = Base64.getDecoder().decode(sSrc); // 先用Base64解密
            byte[] original = cipher.doFinal(encrypted1);
            return new String(original, StandardCharsets.UTF_8);
        } catch (Exception ex) {
            throw ex;
        }
    }
}


2.3.4. 结构体展示

参数

类型

描述

示例

event_id

string

推送事件类型

xd.ticket.result

event_time

int64

消息推送时间

1670240307806

version

string

推送接口的版本信息

v0.0.1

data

object

对应的消息内容


data.version

string

推送事件的版本信息

v0.0.1

data.message_type

string

工单变更类型

ticket_create/ticket_update/ticket_comment (创建工单/更新工单/评论(备注)工单)

data.ticket_entry_id

int

工单的id

1

data.ticket_seq

int

工单编号

1

data.ticket_type_id

int

工单类型id

1

data.category

string

一级分类名称

仓库

data.ticket_type

string

二级工单类型名称

换货

data.ticket_status

string

工单状态

待分配

data.cf_values

[]object

对应工单类型管理中当前二级工单类型中的组件

{

"name": "店铺",

"value": "小宏v/淘宝",

"is_changed": true,

"type":"店铺"

}

data.cf_values.name

string

组件名称

店铺

data.cf_values.value

string

填入的值

小宏v/淘宝

data.cf_values.is_changed

boolean

本次变更后,该字段值是否改变

false

data.cf_values.type

string

组件类型

店铺

data.creator_name

string

创建人

张三

data.create_time

int

创建时间

1670240307000

data.update_time

int

更新时间

1670240307000

data.completed_time

int

完成时间

1670240307000

data.source

string

创建来源

后台/小程序/导入/第三方

data.comments

[]object



data.comments.content

string

备注内容

xxx

data.comments.create_time

string

备注时间

1670240307000

data.comments.account_name

string

备注人员账号名称

张三

data.relations

[]object

data.relations.is_parent

boolean

目标是否是本工单父单,false则为子单

data.relations.ticket_entry_id

int

父子关联单的工单id

data.relations.ticket_entry_seq

int

父子关联单的工单序列

示例

{
  "event_id": "xd.ticket.result",
  "event_time": 1670240307806,
  "version":"v0.0.1",
  "data": {
    "version":"v0.0.1",
    "category": "仓库",// 一级工单类型
    "cf_values": [{
      "name": "优先级", // 组件名称 
      "value": "普通", // 组件值
      "type":"优先级", // 组件类型
      "is_changed": true // 本次变更后,该字段值是否改变
    }, {
      "name": "处理人",
      "value": "待分配",
      "type":"处理人"
    }, {
      "name": "补发原因",
      "value": "破损",
      "type":"下拉"
    }],
    "relations":[
      {
        "is_parent":false,
        "ticket_entry_id":370198,
        "ticket_entry_seq":56680,
        "ticket_type_id":12118
      }
    ],
    "create_time": 1670240307000, // 创建时间
    "creator_name": "杜可风按",// 创建人
    "enterprise_id": "q-610a49e0db1d8875d8980e41",
    "source": "第三方", // 工单创建来源
    "ticket_entry_id": 1, // 工单id
    "ticket_seq": 1, // 工单编号
    "ticket_status": "已完成",// 工单状态
    "ticket_type": "换货", // 二级工单类型
    "ticket_type_id": 1,// 工单类型id
    "update_time": 1670240307000,// 更新时间
    "message_type": "ticket_update",// 推送事件类型
    "comments":[{
      "content":"备注内容",
      "create_time":1670240307000,//备注时间
      "account_name":"张三"//备注人
    }]
  }
}

3. API接口

3.1. 创建工单xd.ticket.create

  1. method:xd.ticket.create
  2. 参数:

参数

类型

必填

描述

示例值

enterprise_id

string

企业ID

1234567890

operator

string

操作人,对应青鸟中人员账号名称

张三

category

string

一级分类名称

仓库

ticket_type

string

二级工单类型名称

换货

ticket_status

string

工单状态

待分配

cf_values


[]object



对应工单类型管理中当前二级工单类型中的组件,传入要创建或更新的组件的值

(填入值可参考 5.工单传入参数规则)


{

"name": "店铺",

"value": "小宏v/淘宝"

}

cf_values.name

string

组件名称

店铺

cf_values.value

string

填入的值

小宏v/淘宝

version

string

版本

v0.0.1

relations

[]object

父子单关系。不填则不关联父子关系

[{

"is_parent":true,

"ticket_entry_id":370000

}]

is_parent:对方是否是本工单父工单,false则为子工单。

支持关联最多三个工单。

  1. 示例
###创建工单
{
  "version":"v0.0.1",
  "enterprise_id": "1234567890", 
  "operator": "张三", 
  "category": "仓库", 
  "ticket_type": "换货", 
  "cf_values": [{
    "name": "店铺",
    "value": "小宏v/淘宝"
  },{ 
    "name": "订单号",
    "value": "2245853052090782746"
  }]
}

@return
{
  "code": 0,
  "error_message": "",
  "data": {
    "code": 0,
    "data": {
      "data": {
        "fields": [
          "店铺"
        ],
        "ticket_entry_id": 1, // 标识工单唯一的id,可用于更新工单
        "ticket_seq":2 // 工单编号,可用于web页面查询工单编号
      },
      "err_code": 10204,
      "message": "存在非必填组件,字段因不匹配被忽略"
    }
  }
}

3.2. 更新工单xd.ticket.update

  1. method:xd.ticket.update
  2. 参数:

参数

类型

必填

描述

示例值

enterprise_id

string

企业ID

1234567890

operator

string

操作人

张三

ticket_entry_id

int

需更新工单的id,由创建接口返回

1

ticket_status

string

工单状态

待分配

cf_values


[]object



对应工单类型管理中当前二级工单类型中的组件,传入要创建或更新的组件的值

(填入值可参考 5.工单传入参数规则)

{

"name": "店铺",

"value": "小宏v/淘宝"

}

cf_values.name

string


组件名称

店铺

cf_values.value

string


填入的值

小宏v/淘宝

comment

string

备注内容

备注内容

version

string

版本

v0.0.1

silent

boolean

是否推送第三方消息

true

relations

[]object

更新父子单关系。不填或空则为删除父子单关系

[{

"is_parent":true,

"ticket_entry_id":370000

}]

is_parent:对方是否是本工单父工单,false则为子工单。

  1. 示例
###更新工单
{
    "version":"v0.0.1",
    "enterprise_id": "1234567890", 
    "operator": "张三",
    "ticket_entry_id":1,
    "cf_values": [{
        "name": "订单号", 
        "value": "2245853052090782746" 
    }]
}

@return
{
    "code": 0,
    "error_message": "",
    "data": {
        "code": 0,
        "data": {
            "data": [],
            "err_code": 10102,
            "message": "工单状态不存在"
        }
    }
}

3.3. 查询工单xd.ticket.list

  1. method:xd.ticket.list
  2. 参数:

参数

类型

必填

描述

示例值

enterprise_id

string

企业ID

1234567890

limit

Int

默认值10,不超过1000

10

skip

int

默认值0

0

category

string

一级分类名称

仓库

ticket_type

string

二级工单类型名称

换货

create_time_begin

int

工单创建开始时间(毫秒级时间戳)

1670083200000

create_time_end

int

工单创建截止时间(毫秒级时间戳)

1670169599999

update_time_begin

int

工单更新开始时间(毫秒级时间戳)

1670083200000

update_time_end

int

工单更新截止时间(毫秒级时间戳)

1670169599999

completed_time_begin

int

工单完成开始时间(毫秒级时间戳)

1670083200000

completed_time_end

int

工单完成截止时间(毫秒级时间戳)

1670169599999

ticket_seq

int

工单编号

1

ticket_status

[]string

工单状态

["已关闭","已完成"]

version

string

版本

v0.0.1

cf_values


[]object


对应工单类型管理中当前二级工单类型中的组件,传入要创建或更新的组件的值

(填入值可参考 5.工单传入参数规则)


{

"name": "订单号",

"value": "123456",

"type": "订单信息"

}

cf_values.name

string

组件名称

订单号

cf_values.value

string

填入的值

123456

cf_values.type

string

组件类型

订单信息

search_relations

boolean

查询父子单时必填

是否查询父子单关系

true: 查询父子单关系。可跨普通/批量打款查询工单

ticket_entry_ids

[]int

查询父子单时必填

父子单工单id

说明:
查询工单列表时仅返回满足条件的工单及其父子单id,

需要通过父子单id进一步查询工单列表获取父子单详情。

  1. 示例:
###查询工单
{
  "version":"v0.0.1",
  "enterprise_id": "1234567890", 
  "category":"仓库",
  "ticket_type": "换货",
  "limit":30,
  "skip":60,
  "cf_values": [
    {
      "type": "订单信息",
      "name": "订单信息",
      "value": "123"
    }
  ]
}

@return
{
  "code": 0,
  "error_message": "",
  "data": {
    "code": 0,
    "data": {
      "data": {
        "count": 8536,
        "entries": [{
          "category": "群对接",// 一级工单类型
          "cf_values": [{
            "name": "处理人1",// 组件名称 
            "value": "22222" // 组件值
          },{
            "name": "优先级22",
            "value": "普通"
          },{
            "name": "多选14",
            "value": "[]"
          },{
            "name": "多级下拉18",
            "value": "["11111","234543"]"
          },{
            "name": "订单信息10",
            "value": "{"order_id":"1730441211855860970"}"
          },{
            "name": "订单金额",
            "value": "0.01"
          },{
            "name": "顾客昵称23",
            "value": "tb43288734"
          },{
            "name": "店铺",
            "value": "不像实力派/淘宝"
          },{
            "name": "平台",
            "value": "淘宝"
          },{
            "name": "收货人手机号3",
            "value": "15775555555"
          },{
            "name": "发货仓库19",
            "value": "成都"
          },{
            "name": "下单时间7",
            "value": "1667875139000"
          }],
          "relations":[
            {"is_parent":true,"ticket_entry_id":95,"ticket_entry_seq":25,"ticket_type_id":93}
          ],
          "create_time": 1680854756000,// 创建时间
          "creator_name": "杜可风按",// 创建人
          "source": "后台", // 创建工单来源
          "ticket_entry_id": 5761752,// 工单id
          "ticket_seq": 17720,//工单编号
          "ticket_status": "待受理",// 工单状态
          "ticket_type": "群对接(全组件)",// 二级工单类型
          "ticket_type_id": 5876,
          "update_time": 1680854757000,// 更新时间
          "comments":[{
            "content":"备注内容",
            "create_time":1670240307000,//备注时间
            "account_name":"张三"//备注人
          }]
        }
        ]
      }
    }
  }
}

3.4. 查询批量打款xd.ticket.batch_pay_list

  1. method:xd.ticket.batch_pay_list
  2. 参数:

参数

类型

必填

描述

示例值

enterprise_id

string

企业ID

1234567890

limit

Int

默认值10,不超过1000

10

skip

int

默认值0

0

category

string

一级分类名称

仓库

ticket_type

string

二级工单类型名称

换货

create_time_begin

int

工单创建开始时间(毫秒级时间戳)

1670083200000

create_time_end

int

工单创建截止时间(毫秒级时间戳)

1670169599999

ticket_seq

int

工单编号

1

pay_time_begin

int

支付开始时间(毫秒级时间戳)

1670083200000

pay_time_end

int

支付截止时间(毫秒级时间戳)

1670169599999

ticket_status

[]string

工单状态

["待审核"]

version

string

版本

v0.0.1

cf_values

[]object


对应工单类型管理中当前二级工单类型中的组件,传入要创建或更新的组件的值

(填入值可参考 5.工单传入参数规则)

{

"name": "订单号",

"value": "123456",

"type": "订单信息"

}

cf_values.name

string

组件名称

订单号

cf_values.value

string

填入的值

123456

cf_values.type

string

组件类型

订单信息

  1. 示例:
###查询工单
{
  "version":"v0.0.1",
  "enterprise_id": "1234567890", 
  "category":"仓库",
  "ticket_type": "换货",
  "limit":30,
  "skip":60,
  "cf_values": [
    {
      "type": "订单信息",
      "name": "订单信息",
      "value": "123"
    }
  ]
}

@return
{
  "code": 0,
  "error_message": "",
  "data": {
    "count":2100,
    "entries":[{
      "ticket_entry_id": 330080, // 工单id
      "ticket_seq":321321, //工单编号
      "category": "全组件-组件",// 一级工单类型
      "ticket_type": "全部组件", // 二级工单类型
      "creator_name": "杜可风按",// 创建人
      "ticket_status": "已完成",// 工单状态
      "create_time": 1670240307000, // 创建时间
      "update_time": 1670240307000// 更新时间
      "pay_account":"908392819@126.com", // 付款支付宝账号
      "pay_time":1670240307000, //付款时间
      "pay_operator":"小王", //付款人
      "cf_values": [{
        "name": "优先级",// 组件名称 
        "value": "普通" // 组件值
      }, {
        "name": "处理人",
        "value": "待分配"
      }],
      "comments":[{
        "content":"备注内容",
        "create_time":1670240307000,//备注时间
        "account_name":"张三"//备注人
      }]
    }]
  }
}

4. 订阅事件

4.1. 工单变更消息xd.ticket.result

将消费者通过小程序、客服通过青鸟工单后台、导入创建或更新的工单推送消息到第三方。

  1. sub_event_ids:xd.ticket.result
  2. 参数:

参数

类型

描述

示例值

ticket_entry_id

int

工单的id

1

ticket_seq

int

工单编号

1

ticket_type_id

int

工单类型id

1

category

string

一级分类名称

仓库

ticket_type

string

二级工单类型名称

换货

ticket_status

string

工单状态

待分配

cf_values

[]object

对应工单类型管理中当前二级工单类型中的组件

{

"name": "店铺",

"value": "小宏v/淘宝",

"is_changed": true

}

cf_values.name

string

组件名称

店铺

cf_values.value

string

填入的值

小宏v/淘宝

cf_values.is_changed

boolean

本次变更后,该字段值是否改变

false

creator_name

string

创建人

张三

create_time

int

创建时间

1670240307000

source

string

创建来源

后台/小程序/导入/第三方

message_type

string

推送事件类型

ticket_create/ticket_update/ticket_comment (创建工单/更新工单/评论(备注)工单)

  1. 示例:
{
  "event_id": "xd.ticket.result",
  "event_time": 1670240307806,
  "data": {
    "category": "仓库",// 一级工单类型
    "cf_values": [{
      "name": "优先级",// 组件名称 
      "value": "普通", // 组件值
      "is_changed": true // 本次变更后,该字段值是否改变
    }, {
      "name": "处理人",
      "value": "待分配"
    }],
    "create_time": 1670240307000, // 创建时间
    "creator_name": "杜可风按",// 创建人
    "enterprise_id": "q-610a49e0db1d8875d8980e41",
    "source": "第三方", // 工单创建来源
    "ticket_entry_id": 330080, // 工单id
    "ticket_seq": 17632, // 工单编号
    "ticket_status": "已完成",// 工单状态
    "ticket_type": "换货", // 二级工单类型
    "ticket_type_id": 1480,// 工单类型id
    "update_time": 1670240307000,// 更新时间
    "message_type": "ticket_update",// 推送事件类型
    "comments":[{
        "content":"备注内容",
        "create_time":1670240307000,//备注时间
        "account_name":"张三"//备注人
    }]
  },
  "appkey": ""
}

5. 工单传入参数规则

在创建工单、更新工单时,传入组件值的数据规则与工单类型中组件类型对应的,对应规则如下,若表格中无对应的组件,则不支持传入。

5.1. 版本v0.0.1

组件类型

新建、更新工单 传入示例

查询工单 传入示例

说明

数据来源

电商组件


店铺

{"name": "组件名称","value": "测试店铺非卖品/淘宝"}

{"name": "组件名称","value": "["测试店铺非卖品/淘宝"]","type": "店铺"}

店铺管理中已有的店铺,格式:店铺名称/平台

平台

{"name": "组件名称","value": "淘宝"}

{"name": "组件名称","value": "["淘宝"]","type":"平台"}

青鸟协同里平台组件已有的平台

订单信息

订单号

{"name": "组件名称","value": "{"order_id":"20220000000","sku_ids":["1000000222"],"goods_ids":["9999000000"]}"}


{"name": "组件名称","value": "20220000000","type":"订单信息"}

order_id为订单号


订单商品



sku_id为商品SKU的ID,goods_id为商品的ID

传入SKU的ID时,将勾选对应的SKU商品;传入商品ID时,可能出现多个不同SKU的商品,将勾选商品ID对应的该商品在订单中的所有SKU商品。



订单金额

{"name": "组件名称","value": "1.02"}

{"name": "组件名称","value": "[1,2]","type":"订单金额"}

传入不为空时则为传入的内容,没有则默认按照订单自动回填;任意数字(格式支持最多3位小数)


顾客昵称

{"name": "组件名称","value": "tb425743344"}

{"name": "组件名称","value": "tb425743344","type":"顾客昵称"}

传入不为空时则为传入的内容,没有则默认通过订单号自动回填


商品

{"name": "组件名称","value": "[{"outer_sku_id":"PT004","item_name":"衣架,二手衣架","num":1}]"}



ERP商品

value.outer_sku_id 为商家编码

value.item_name 为商品名称

value.num 为商品数据


发货仓库

{"name": "组件名称","value": "成都"}

{"name": "组件名称","value": "成都","type":"发货仓库"}

传入不为空时则为传入的内容,没有则默认按照订单自动回填;请填写工单系统内发货仓库组件中已有的选项


下单时间

{"name": "组件名称","value": "1667977603000"}


传入不为空时则为传入的内容,没有则默认按照订单自动回填;填写13位的时间戳


收货人信息套件

收货人

{"name": "组件名称","value": "张三"}

{"name": "组件名称","value": "张三","type":"收货人"}

收货人姓名


收货人手机号

{"name": "组件名称","value": "180000000"}

{"name": "组件名称","value": "180000000","type":"收货人手机号"}


180000000


收货人地址

{"name": "组件名称","value": "{"province":{"name":"四川省"},"town":{"name":"成都市"},"district":{"name":"双流区"},"street":"中和街道","detail":"中和街道朝阳路229号城南春天"}"}

{"name": "组件名称","value": "四川省成都市双流区中和街道朝阳路229号城南春天","type":"收货人地址"}


省,市,区,街道,详细地址

(每个字段都需填写,地址信息才能生效)


物流信息

{"name":"组件名称","value":"[{"logistics_company":"中通","logistics_no":"123465489"}]"}


logistics_company为承运商

logistics_no为物流单号


基础组件


单行文本

{"name": "组件名称","value": "单行文本"}

{"name": "组件名称","value": "单行文本","type": "单行文本"}

一则单行文本


多行文本

{"name": "组件名称","value": "多行文本"}

{"name": "组件名称","value": "多行文本","type":"多行文本"}

一则多行文本


日期

{"name": "组件名称","value": "1667977603000"}


{"name": "组件名称","value": "[1667977603000,1667977603000]","type":"日期"}

填写毫秒级时间戳


日期&时间

{"name": "组件名称","value": "1667977603000"}

{"name": "组件名称","value": "[1667977603000,1667977603000]","type":"日期&时间"}

填写毫秒级时间戳



下拉

{"name": "组件名称","value": "下拉选项"}

{"name": "组件名称","value": "["下拉选项"]","type":"下拉"}

请填写该组件内已有的选项


多级下拉

{"name": "组件名称","value": "["第1层选项","第2层选项"]"}

{"name": "组件名称","value": "["第1层选项>第2层选项"]","type":"多级下拉"}

请填写该组件内已有的选项


单选

{"name": "组件名称","value": "单选选项"}

{"name": "组件名称","value": "["单选选项"]","type":"单选"}

请填写该组件内已有的选项


多选

{"name": "组件名称","value": "["第1个多选选项","第2个多选选项"]"}

{"name": "组件名称","value": "["第1个多选选项","第2个多选选项"]","type":"多选"}

请填写该组件内已有的选项


NPS量表

{"name": "组件名称","value": "NPS选项值"}

{"name": "组件名称","value": "["NPS选项值"]","type":"NPS量表"}

请填写该组件内已有的选项

数值

{"name": "组件名称","value": "1.2"}


{"name": "组件名称","value": "[1,2]","type":"数值"}


任意数字(格式支持最多3位小数)



上传图片


{"name": "组件名称","value":"[{"name":"f5dd25c2527841ec883df8f3ceadc983.jpg","url":"https://wangcai-client-new.oss-cn-hangzhou.aliyuncs.com/1647949042024_f5dd25c2527841ec883df8f3ceadc983.jpg"}]"}



value.name 为图片名称

value.url 为图片的地址



附件

{"name": "组件名称","value":"[{"file_name":"f5dd25c2527841ec883df8f3ceadc983.jpg","url":"https://wangcai-client-new.oss-cn-hangzhou.aliyuncs.com/1647949042024_f5dd25c2527841ec883df8f3ceadc983.jpg"}]"}


value.file_name为附件名称

value.url 为附件的地址


优先级

{"name": "组件名称","value": "普通"}

{"name": "组件名称","value": "["普通"]","type":"优先级"}

普通/紧急/非常紧急


处理人

{"name": "组件名称","value": "处理人"}

{"name": "组件名称","value": "["处理人"]","type":"处理人"}

请填写工单系统内成员昵称或保持空白


工单状态

作为ticket_status参数传入,”ticket_status“:"待分配"

作为ticket_status参数传入,”ticket_status“:["待分配"]

待分配/待受理/处理中/已完成/已关闭

审批驳回/待审批/待支付/支付中/已支付/支付失败/已关闭/已支付处理中


跟进时间

{"name": "组件名称","value": "1667977603000"}

{"name": "组件名称","value": "[1667977603000,1667977603000]","type":"跟进时间"}

填写13位的时间戳



智能组件


群对接

{"name": "组件名称","value": "中通对接群"}


请填写工单系统中已配置的物流对接群

旺店通智能组件


{"name": "组件名称","value": "[{"order_type":"plat","erp_order_id":"JY202209210235","warehouse_code":"001","logistics_company":"淘宝-中通","logistics_no":"","create_time":1663750499000,"got_time":0,"sign_time":0,"estimate_profit":0.0,"latest_logistics_info":"","goods":[{"goods_name":"床上四件套-床单","spec_name":"默认规格","spec_no":"CHUANG-CHUANGDAN","share_price":0.0011,"share_amount":0.0077,"num":7}]}]"}



order_type 订单类型 string

erp_order_id erp单号 string

warehouse_code 发货仓 string

logistics_company 物流公司 string

logistics_no 物流编号 string

create_time 创建时间 int64 13位的时间戳

got_time 揽收时间 int64 13位的时间戳

sign_time 签收时间 int64 13位的时间戳

estimate_profit 预估毛利 float64

latest_logistics_info 最新物流信息 string

goods.goods_name 商品名称 string

goods.spec_name 规格名称 string

goods.spec_no 规格编码 string

goods.share_price 分摊价格 float64

goods.share_amount 分摊总价 float64

goods.num 数量 int64


6. 错误码

错误码分为两部分:

  • http 响应体
  • http body 返回的 json 中的 code
  • 其中有一些错误会在 http body 返回的 json 中的 data 中,返回业务具体信息。如:创建工单时,存在必填项未填时,data中返回具体哪些组件未填
{
  "code": 0,
  "error_message": "",
  "data": {
    "code": 0,
    "data": {
      "data": {
        "fields": [
          "店铺"
        ],
        "ticket_entry_id": 1
      },
      "err_code": 10204,
      "message": "存在非必填组件,字段因不匹配被忽略"
    }
  }
}

6.1. http 响应体

外层code值

含义

0

成功,可以使用data内的业务内容

非0

各种网关层错误,原因会填写error_message字段

6.2. 内层code

含义

data

10001

找不到对应的工单类型

10002

存在多个工单类型

10003

该工单类型不支持创建工单

10101

工单ID不存在

10102

工单状态不存在

10103

该企业不存在

10104

该操作人不存在

10105

该版本不存在

10201

缺少必填项组件

{"fields":["组件名称1","组件名称2"]}

10202

必填组件,但未定义规则,不支持传入

{"fields":["组件名称1","组件名称2"]}

10203

必填组件字段不匹配

{"fields":["组件名称1","组件名称2"]}

10204

存在非必填组件字段不匹配(其他能匹配到的字段仍然能成功更新)

{"fields":["组件名称1","组件名称2"]}

10301

此工单当前状态下不允许编辑

10302

当前账号无权限编辑工单