kdn-sdk 0.2.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kdn/__init__.py +7 -0
- kdn/api/__init__.py +3 -0
- kdn/api/comm.py +100 -0
- kdn/api/kdn.py +110 -0
- kdn/api/logistic.py +96 -0
- kdn/api/order.py +413 -0
- kdn/api/query.py +173 -0
- kdn/api/service.py +261 -0
- kdn/api/utils.py +5 -0
- kdn/data/direct.csv +7 -0
- kdn/data/eorder.csv +4 -0
- kdn/data/express.csv +608 -0
- kdn/data/exptype.csv +129 -0
- kdn/data/kdn.csv +6 -0
- kdn/data/month.csv +2 -0
- kdn/data/offline.csv +11 -0
- kdn/data/preorder.csv +23 -0
- kdn/data/templates.csv +84 -0
- kdn/data/trans.csv +35 -0
- kdn/tests/__init__.py +0 -0
- kdn/tests/test_comm.py +50 -0
- kdn/tests/test_logistic.py +30 -0
- kdn/tests/test_onroad.py +23 -0
- kdn/tests/test_order.py +45 -0
- kdn/tests/test_query.py +31 -0
- kdn/tests/test_service.py +77 -0
- kdn_sdk-0.2.9.dist-info/METADATA +81 -0
- kdn_sdk-0.2.9.dist-info/RECORD +31 -0
- kdn_sdk-0.2.9.dist-info/WHEEL +5 -0
- kdn_sdk-0.2.9.dist-info/top_level.txt +1 -0
- kdn_sdk-0.2.9.dist-info/zip-safe +1 -0
kdn/__init__.py
ADDED
kdn/api/__init__.py
ADDED
kdn/api/comm.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2020-03-25
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
# 接口调用公共类
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from hashlib import md5
|
|
9
|
+
import base64
|
|
10
|
+
from urllib.parse import urlencode, quote_plus, quote
|
|
11
|
+
import requests
|
|
12
|
+
import csv
|
|
13
|
+
from copy import deepcopy
|
|
14
|
+
import logging
|
|
15
|
+
|
|
16
|
+
_logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Comm(object):
|
|
20
|
+
|
|
21
|
+
def __get__(self, instance, owner):
|
|
22
|
+
self.client_id = instance.client_id
|
|
23
|
+
self.api_key = instance.api_key
|
|
24
|
+
self.sandbox = instance.sandbox
|
|
25
|
+
self.timeout = instance.timeout
|
|
26
|
+
self.url = instance.url
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
def _get_request_header(self):
|
|
30
|
+
"""
|
|
31
|
+
获取固定的header
|
|
32
|
+
"""
|
|
33
|
+
header = {
|
|
34
|
+
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
|
35
|
+
}
|
|
36
|
+
return header
|
|
37
|
+
|
|
38
|
+
def _mask_identifier(self, value):
|
|
39
|
+
value = str(value or "")
|
|
40
|
+
if len(value) <= 4:
|
|
41
|
+
return "*" * len(value)
|
|
42
|
+
return f"{value[:2]}***{value[-2:]}"
|
|
43
|
+
|
|
44
|
+
def _sign(self, data):
|
|
45
|
+
"""
|
|
46
|
+
签名逻辑
|
|
47
|
+
"""
|
|
48
|
+
to_sign = f"{data}{self.api_key}".encode(
|
|
49
|
+
"utf-8")
|
|
50
|
+
sign = base64.b64encode(
|
|
51
|
+
md5(to_sign).hexdigest().encode("utf-8")).decode("utf-8")
|
|
52
|
+
return sign
|
|
53
|
+
|
|
54
|
+
def _remove_none(self, d):
|
|
55
|
+
dx = deepcopy(d)
|
|
56
|
+
for k, v in d.items():
|
|
57
|
+
if v and type(v) is dict:
|
|
58
|
+
r = self._remove_none(v)
|
|
59
|
+
if r:
|
|
60
|
+
dx[k] = r
|
|
61
|
+
else:
|
|
62
|
+
dx[k] = ''
|
|
63
|
+
if v and type(v) is list:
|
|
64
|
+
for i in range(len(v)):
|
|
65
|
+
dx[k][i] = self._remove_none(v[i])
|
|
66
|
+
if not v:
|
|
67
|
+
dx[k] = ''
|
|
68
|
+
return dx
|
|
69
|
+
|
|
70
|
+
def post(self, request_type, data, url=None, timeout=None):
|
|
71
|
+
"""
|
|
72
|
+
提交请求
|
|
73
|
+
params:
|
|
74
|
+
data: 提交的数据
|
|
75
|
+
"""
|
|
76
|
+
payload = json.dumps(self._remove_none(
|
|
77
|
+
data), separators=(',', ':'), ensure_ascii=False)
|
|
78
|
+
request_data = {
|
|
79
|
+
"RequestType": request_type,
|
|
80
|
+
"EBusinessID": self.client_id,
|
|
81
|
+
"RequestData": quote_plus(payload),
|
|
82
|
+
"DataSign": self._sign(payload),
|
|
83
|
+
"DataType": 2
|
|
84
|
+
}
|
|
85
|
+
endpoint = url or self.url
|
|
86
|
+
request_timeout = self.timeout if timeout is None else timeout
|
|
87
|
+
_logger.debug(
|
|
88
|
+
"KDN-SDK POST request_type=%s ebusiness_id=%s endpoint=%s payload_bytes=%s",
|
|
89
|
+
request_type,
|
|
90
|
+
self._mask_identifier(self.client_id),
|
|
91
|
+
endpoint,
|
|
92
|
+
len(payload.encode("utf-8")),
|
|
93
|
+
)
|
|
94
|
+
response = requests.post(
|
|
95
|
+
endpoint,
|
|
96
|
+
data=request_data,
|
|
97
|
+
headers=self._get_request_header(),
|
|
98
|
+
timeout=request_timeout,
|
|
99
|
+
)
|
|
100
|
+
return response.json()
|
kdn/api/kdn.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2020-03-25
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from .comm import Comm
|
|
6
|
+
from .query import Query
|
|
7
|
+
from .order import Order
|
|
8
|
+
from .service import Service
|
|
9
|
+
from .logistic import Logistic
|
|
10
|
+
import csv
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
SANDBOXURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json"
|
|
15
|
+
URL = "http://api.kdniao.com"
|
|
16
|
+
DEFAULT_TIMEOUT = 15
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class KDN(object):
|
|
20
|
+
|
|
21
|
+
def __init__(self, client_id, api_key, sandbox=False, timeout=DEFAULT_TIMEOUT):
|
|
22
|
+
"""
|
|
23
|
+
params:
|
|
24
|
+
client_id: 商户ID,
|
|
25
|
+
api_key: API KEY,
|
|
26
|
+
sandbox: 是否沙箱测试环境
|
|
27
|
+
timeout: 请求超时时间,单位秒
|
|
28
|
+
"""
|
|
29
|
+
self.client_id = client_id
|
|
30
|
+
self.api_key = api_key
|
|
31
|
+
self.sandbox = sandbox
|
|
32
|
+
self.timeout = timeout
|
|
33
|
+
self.url = SANDBOXURL if self.sandbox else URL
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def get_express_list(cls):
|
|
37
|
+
"""
|
|
38
|
+
获取支持的公司列表
|
|
39
|
+
返回包含代码和名称的字典
|
|
40
|
+
"""
|
|
41
|
+
express = {}
|
|
42
|
+
csv_path = os.path.dirname(os.path.dirname(__file__))
|
|
43
|
+
with open(os.path.join(csv_path, "data/express.csv"),encoding='UTF-8') as f:
|
|
44
|
+
for row in csv.reader(f):
|
|
45
|
+
express[row[1].strip()] = row[0].strip()
|
|
46
|
+
return express
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def get_eorder_express(cls):
|
|
50
|
+
"""
|
|
51
|
+
获取支持电子面单的物流公司
|
|
52
|
+
"""
|
|
53
|
+
codes = []
|
|
54
|
+
filelist = ('direct.csv', 'eorder.csv',
|
|
55
|
+
'kdn.csv', 'month.csv', 'offline.csv')
|
|
56
|
+
csv_path = os.path.dirname(os.path.dirname(__file__))
|
|
57
|
+
for file in filelist:
|
|
58
|
+
with open(os.path.join(csv_path, f"data/{file}"),encoding='UTF-8') as f:
|
|
59
|
+
for row in csv.reader(f):
|
|
60
|
+
codes.append(row[0])
|
|
61
|
+
return set(list(codes))
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def get_templates(cls):
|
|
65
|
+
"""
|
|
66
|
+
获取电子面单模板
|
|
67
|
+
"""
|
|
68
|
+
templates = []
|
|
69
|
+
csv_path = os.path.dirname(os.path.dirname(__file__))
|
|
70
|
+
with open(os.path.join(csv_path, "data/templates.csv"),encoding='UTF-8') as f:
|
|
71
|
+
for row in csv.reader(f):
|
|
72
|
+
templates.append(tuple(row))
|
|
73
|
+
return templates
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def get_express_types(self):
|
|
77
|
+
"""获取快递公司业务类型"""
|
|
78
|
+
types = []
|
|
79
|
+
csv_path = os.path.dirname(os.path.dirname(__file__))
|
|
80
|
+
with open(os.path.join(csv_path, "data/exptype.csv"),encoding='UTF-8') as f:
|
|
81
|
+
for row in csv.reader(f):
|
|
82
|
+
types.append(tuple(row))
|
|
83
|
+
return types
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def get_preorder_express(self):
|
|
87
|
+
"""get express list which supported preordering"""
|
|
88
|
+
expresses =[]
|
|
89
|
+
csv_path = os.path.dirname(os.path.dirname(__file__))
|
|
90
|
+
with open(os.path.join(csv_path, "data/preorder.csv"),encoding='UTF-8') as f:
|
|
91
|
+
for row in csv.reader(f):
|
|
92
|
+
expresses.append(row[0])
|
|
93
|
+
return expresses
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def get_trans_types(self):
|
|
98
|
+
"""get express trans types"""
|
|
99
|
+
transtypes =[]
|
|
100
|
+
csv_path = os.path.dirname(os.path.dirname(__file__))
|
|
101
|
+
with open(os.path.join(csv_path, "data/trans.csv"),encoding='UTF-8') as f:
|
|
102
|
+
for row in csv.reader(f):
|
|
103
|
+
transtypes.append(row)
|
|
104
|
+
return transtypes
|
|
105
|
+
|
|
106
|
+
comm = Comm()
|
|
107
|
+
query = Query()
|
|
108
|
+
order = Order()
|
|
109
|
+
service = Service()
|
|
110
|
+
logistic = Logistic()
|
kdn/api/logistic.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2021-11-29
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
# 物流跟踪 API
|
|
6
|
+
|
|
7
|
+
from requests.api import get
|
|
8
|
+
from .query import Query
|
|
9
|
+
# from .comm import Comm
|
|
10
|
+
# UPGRADE = {
|
|
11
|
+
# 1002: 8001,
|
|
12
|
+
# }
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# def logistic(func):
|
|
16
|
+
# def wrapper(*args, **kwargs):
|
|
17
|
+
# print(getattr(func,'request_type'))
|
|
18
|
+
# func.request_type = UPGRADE[func.request_type]
|
|
19
|
+
# return func(*args, **kwargs)
|
|
20
|
+
# return wrapper
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Logistic(Query):
|
|
24
|
+
|
|
25
|
+
def __init__(self, *args, **kwargs):
|
|
26
|
+
self.express_code = 8001
|
|
27
|
+
self.subsribe_code = 8008
|
|
28
|
+
|
|
29
|
+
# def get_express_routes(self, *args, **kwargs):
|
|
30
|
+
# """
|
|
31
|
+
# 获取即时物流轨迹
|
|
32
|
+
# params:
|
|
33
|
+
# shipper_code: 快递公司编码
|
|
34
|
+
# logistic_code: 快递单号
|
|
35
|
+
# order_code: 订单编号
|
|
36
|
+
# customer_name: ShipperCode 为 JD,必填,对应京东的青龙配送编码,也叫商家编码.
|
|
37
|
+
# """
|
|
38
|
+
# func = Query.get_express_routes
|
|
39
|
+
# func.request_type = 8001
|
|
40
|
+
# return func(self, *args, **kwargs)
|
|
41
|
+
|
|
42
|
+
# def subscribe_express_routes(self, *args, **kwargs):
|
|
43
|
+
# """
|
|
44
|
+
# 轨迹订阅接口
|
|
45
|
+
# params:
|
|
46
|
+
# shipper_code: 快递公司编码
|
|
47
|
+
# logistic_code: 快递单号
|
|
48
|
+
# recevier_name: 收件人姓名
|
|
49
|
+
# province_name: 收件人省份
|
|
50
|
+
# city_name: 收件人城市
|
|
51
|
+
# district_name: 收件人区域
|
|
52
|
+
# address: 收件人详细地址
|
|
53
|
+
# sender_name: 发件人姓名
|
|
54
|
+
# sender_tel: 发件人电话
|
|
55
|
+
# sender_mobile: 发件人手机 (电话或手机必填一个)
|
|
56
|
+
# sender_province: 发件人省份
|
|
57
|
+
# sender_city:发件人城市
|
|
58
|
+
# sender_district: 发件人区域
|
|
59
|
+
# sender_address: 发件人详细地址
|
|
60
|
+
# recevier_tel: 收件人电话
|
|
61
|
+
# receiver_mobile: 收件人手机 (电话或手机必填一个)
|
|
62
|
+
# member_id: ERP 系统、电商平台等系统或平台类型用户的会员ID或店铺账号等唯一性标识,用于区分其用户
|
|
63
|
+
# warehouse_id: 仓库标志
|
|
64
|
+
# customer_name: ShipperCode 为 JD,必填,对应京东的青龙配送编码,也叫商家编码
|
|
65
|
+
# order_code: 订单编号
|
|
66
|
+
# month_code: 月结单号
|
|
67
|
+
# pay_type: 运费支付方式:1-现付,2-到付,3-月结,4- 第三方付(仅 SF、KYSY 支持)
|
|
68
|
+
# exp_type:详细快递类型
|
|
69
|
+
# cost: 快递运费
|
|
70
|
+
# other_cost: 其他费用
|
|
71
|
+
# receiver_company: 收件人公司
|
|
72
|
+
# receiver_post_code:收件人邮编
|
|
73
|
+
# sender_company: 发件人公司
|
|
74
|
+
# sender_post_code: 发件人邮编
|
|
75
|
+
# is_notice:是否通知快递员上门揽件 0-通知,1-不通知,不填则 默认为 1
|
|
76
|
+
# start_date: 上门揽件时间段,格式:YYYY-MM-DD HH24:MM:SS
|
|
77
|
+
# end_date: 上门揽件时间段
|
|
78
|
+
# weight: 重量
|
|
79
|
+
# quantity: 包裹数量
|
|
80
|
+
# volume: 体积
|
|
81
|
+
# remark: 备注
|
|
82
|
+
# is_sender_message: 是否订阅短信 0-不需要,1-需要
|
|
83
|
+
# service_name:增值服务名称
|
|
84
|
+
# service_value: 增值服务值
|
|
85
|
+
# service_customer_id: 增值服务客户id
|
|
86
|
+
# goods_name: 商品名称
|
|
87
|
+
# goods_code: 商品编码
|
|
88
|
+
# goods_quantity:商品数量
|
|
89
|
+
# goods_price: 商品价格
|
|
90
|
+
# goods_weight:商品重量
|
|
91
|
+
# goods_desc:商品描述
|
|
92
|
+
# goods_vol: 商品体积
|
|
93
|
+
# """
|
|
94
|
+
# func = Query.subscribe_express_routes
|
|
95
|
+
# func.request_type = 8008
|
|
96
|
+
# return func(self, *args, **kwargs)
|