ey-commerce-lib 1.0.5__tar.gz → 1.0.7__tar.gz
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.
Potentially problematic release.
This version of ey-commerce-lib might be problematic. Click here for more details.
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/PKG-INFO +3 -1
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/pyproject.toml +26 -24
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/main.py +1 -1
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/parser/order.py +2 -2
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/constant/response.py +8 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/main.py +280 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/parser/order.py +19 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/schemas/query/order.py +101 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/schemas/vo/__init__.py +0 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/schemas/vo/order.py +437 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/model.py +12 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/py.typed +0 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/utils/__init__.py +0 -0
- ey_commerce_lib-1.0.7/src/ey_commerce_lib/utils/close.py +6 -0
- ey_commerce_lib-1.0.7/tests/__init__.py +0 -0
- ey_commerce_lib-1.0.7/tests/dxm/__init__.py +0 -0
- ey_commerce_lib-1.0.7/tests/dxm/test_purchase.py +21 -0
- ey_commerce_lib-1.0.7/uv.lock +884 -0
- ey_commerce_lib-1.0.5/uv.lock +0 -261
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/.gitignore +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/.python-version +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/README.md +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/pytest.ini +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/constant/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/constant/order.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/exception/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/exception/common.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/order.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/parser/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/parser/common.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/parser/purchase.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/parser/warehouse.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/schemas/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/schemas/common.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/schemas/order.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/dxm/schemas/warehouse.py +0 -0
- {ey_commerce_lib-1.0.5/src/ey_commerce_lib/utils → ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller}/__init__.py +0 -0
- {ey_commerce_lib-1.0.5/tests → ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/constant}/__init__.py +0 -0
- {ey_commerce_lib-1.0.5/tests/dxm → ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/parser}/__init__.py +0 -0
- /ey_commerce_lib-1.0.5/src/ey_commerce_lib/py.typed → /ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/schemas/__init__.py +0 -0
- /ey_commerce_lib-1.0.5/tests/dxm/test_purchase.py → /ey_commerce_lib-1.0.7/src/ey_commerce_lib/four_seller/schemas/query/__init__.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/utils/dxm.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/utils/list_util.py +0 -0
- {ey_commerce_lib-1.0.5 → ey_commerce_lib-1.0.7}/src/ey_commerce_lib/utils/str.py +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ey-commerce-lib
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.7
|
|
4
4
|
Summary: eeyoung电商客户端调用封装
|
|
5
5
|
Author-email: 饶奇奇 <1124393197@qq.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: ddddocr>=1.5.6
|
|
7
8
|
Requires-Dist: httpx>=0.28.1
|
|
8
9
|
Requires-Dist: lxml>=5.3.2
|
|
10
|
+
Requires-Dist: playwright>=1.52.0
|
|
9
11
|
Requires-Dist: pydantic>=2.11.3
|
|
10
12
|
Requires-Dist: pytest-asyncio>=0.26.0
|
|
11
13
|
Requires-Dist: pytest>=8.3.5
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "ey-commerce-lib"
|
|
3
|
-
version = "1.0.
|
|
4
|
-
description = "eeyoung电商客户端调用封装"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
authors = [
|
|
7
|
-
{ name = "饶奇奇", email = "1124393197@qq.com" }
|
|
8
|
-
]
|
|
9
|
-
requires-python = ">=3.10"
|
|
10
|
-
dependencies = [
|
|
11
|
-
"pytest-asyncio>=0.26.0",
|
|
12
|
-
"pytest>=8.3.5",
|
|
13
|
-
"httpx>=0.28.1",
|
|
14
|
-
"pydantic>=2.11.3",
|
|
15
|
-
"lxml>=5.3.2",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
build-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
[project]
|
|
2
|
+
name = "ey-commerce-lib"
|
|
3
|
+
version = "1.0.7"
|
|
4
|
+
description = "eeyoung电商客户端调用封装"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "饶奇奇", email = "1124393197@qq.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"pytest-asyncio>=0.26.0",
|
|
12
|
+
"pytest>=8.3.5",
|
|
13
|
+
"httpx>=0.28.1",
|
|
14
|
+
"pydantic>=2.11.3",
|
|
15
|
+
"lxml>=5.3.2",
|
|
16
|
+
"playwright>=1.52.0",
|
|
17
|
+
"ddddocr>=1.5.6",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[pypi]
|
|
25
|
+
username = '__token__'
|
|
26
|
+
password = 'pypi-AgEIcHlwaS5vcmcCJGM0ODMxYjA3LWUwZGEtNDI5MC04ZjFhLWY0NWQ2YjMwMTE3YgACKlszLCIxZmExMDhjZC02MjBjLTQ0ZjQtYmRiYy01ZDc4ZDM0MmY4YzIiXQAABiAX4H1ZB3reItO25PIffe7vOLqyqktJpnevZy_ibdUt6Q'
|
|
@@ -522,7 +522,7 @@ class DxmClient:
|
|
|
522
522
|
validate_price_res = await self.__async_client.post("/dxmPurchasingNote/validatePrice.json", data={
|
|
523
523
|
'ids': ids
|
|
524
524
|
})
|
|
525
|
-
return validate_price_res
|
|
525
|
+
return validate_price_res.json()
|
|
526
526
|
|
|
527
527
|
async def wait_pay_pay_money(self, purchase_id: str):
|
|
528
528
|
"""
|
|
@@ -16,10 +16,10 @@ def get_single_order_item_list(order_element: html.HtmlElement):
|
|
|
16
16
|
for sku_element in sku_element_list:
|
|
17
17
|
sku = sku_element.xpath('.//a[contains(@class, "pairProInfoSku")]/text()')[0]
|
|
18
18
|
img = get_str_list_first_not_blank_or_none(sku_element.xpath('.//img/@data-original'))
|
|
19
|
-
quantity = sku_element.xpath('.//
|
|
19
|
+
quantity = sku_element.xpath('.//span[@class="limingcentUrlpicson"]/following-sibling::span[1]/text()')[0]
|
|
20
20
|
# 价格
|
|
21
21
|
currency, price = \
|
|
22
|
-
sku_element.xpath('.//
|
|
22
|
+
sku_element.xpath('.//span[@class="limingcentUrlpicson"]/parent::*/following-sibling::p[1]/text()')[
|
|
23
23
|
0].split(' ')
|
|
24
24
|
# 变种
|
|
25
25
|
variants = sku_element.xpath(
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import base64
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from httpx import AsyncClient, ReadTimeout
|
|
7
|
+
from playwright.async_api import async_playwright, TimeoutError
|
|
8
|
+
import ddddocr
|
|
9
|
+
|
|
10
|
+
from ey_commerce_lib.four_seller.constant.response import ResponseCodeEnum
|
|
11
|
+
from ey_commerce_lib.four_seller.parser.order import parse_order
|
|
12
|
+
from ey_commerce_lib.four_seller.schemas.query.order import FourSellerOrderQueryModel
|
|
13
|
+
from ey_commerce_lib.four_seller.schemas.vo.order import FourSellerOrderVO
|
|
14
|
+
from ey_commerce_lib.model import Page
|
|
15
|
+
from ey_commerce_lib.utils.close import safe_close
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FourSellerClient:
|
|
21
|
+
|
|
22
|
+
def __init__(self, user_name: str,
|
|
23
|
+
password: str,
|
|
24
|
+
user_token: str,
|
|
25
|
+
login_success_call_back: Callable,
|
|
26
|
+
sem: int = 10):
|
|
27
|
+
self.user_name = user_name
|
|
28
|
+
self.password = password
|
|
29
|
+
self.user_token = user_token
|
|
30
|
+
# 登录成功回调函数
|
|
31
|
+
self.login_success_call_back = login_success_call_back
|
|
32
|
+
# 信号量
|
|
33
|
+
self.__sem = asyncio.Semaphore(sem)
|
|
34
|
+
# 自动登录锁
|
|
35
|
+
self.__login_lock = asyncio.Lock()
|
|
36
|
+
# 当前登录任务的 future
|
|
37
|
+
self.__login_in_progress = None
|
|
38
|
+
# ddddocr实例
|
|
39
|
+
self.__ocr = ddddocr.DdddOcr()
|
|
40
|
+
# 异步客户端
|
|
41
|
+
self.__async_client = AsyncClient(base_url="https://www.4seller.com", cookies={"userToken": user_token},
|
|
42
|
+
headers={
|
|
43
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
44
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 "
|
|
45
|
+
"Safari/537.36"
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
async def __auto_login_4seller(self) -> str:
|
|
49
|
+
"""
|
|
50
|
+
自动登录4seller
|
|
51
|
+
:return: 登录
|
|
52
|
+
"""
|
|
53
|
+
browser = None
|
|
54
|
+
context = None
|
|
55
|
+
page = None
|
|
56
|
+
try:
|
|
57
|
+
async with async_playwright() as p:
|
|
58
|
+
browser = await p.chromium.launch(
|
|
59
|
+
headless=True,
|
|
60
|
+
args=[
|
|
61
|
+
'--start-maximized',
|
|
62
|
+
'--disable-blink-features=AutomationControlled',
|
|
63
|
+
]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
context = await browser.new_context(
|
|
67
|
+
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
68
|
+
"(KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
|
|
69
|
+
viewport={"width": 1920, "height": 1080},
|
|
70
|
+
locale='zh-CN', # 中文环境
|
|
71
|
+
timezone_id='Asia/Shanghai', # 中国时区
|
|
72
|
+
geolocation={"longitude": 116.4074, "latitude": 39.9042}, # 北京位置
|
|
73
|
+
permissions=["geolocation"]
|
|
74
|
+
)
|
|
75
|
+
page = await context.new_page()
|
|
76
|
+
await page.goto('https://www.4seller.com/login.html')
|
|
77
|
+
# 等待元素出现
|
|
78
|
+
await page.locator('.el-input__inner').first.wait_for()
|
|
79
|
+
input_locators = await page.locator('.el-input__inner').all()
|
|
80
|
+
username_input_element = input_locators[0]
|
|
81
|
+
password_input_element = input_locators[1]
|
|
82
|
+
await username_input_element.fill(self.user_name)
|
|
83
|
+
await password_input_element.fill(self.password)
|
|
84
|
+
login_button_element = page.locator('.el-button.sign_up')
|
|
85
|
+
await login_button_element.click()
|
|
86
|
+
# 获取验证码图片
|
|
87
|
+
capcha_code_img_element = page.locator('.el-input__wrapper img')
|
|
88
|
+
await capcha_code_img_element.wait_for()
|
|
89
|
+
captcha_code_input_element = page.locator('[autocomplete="new-password"]')
|
|
90
|
+
login_count = 1
|
|
91
|
+
# 只能允许一百次登录
|
|
92
|
+
while login_count <= 100:
|
|
93
|
+
# 等图片 src 被赋值
|
|
94
|
+
max_attempts = 10
|
|
95
|
+
for attempt in range(max_attempts):
|
|
96
|
+
src = await capcha_code_img_element.get_attribute("src")
|
|
97
|
+
if src and src.strip() != "":
|
|
98
|
+
break
|
|
99
|
+
await page.wait_for_timeout(500)
|
|
100
|
+
else:
|
|
101
|
+
raise Exception("获取不到图片验证码")
|
|
102
|
+
# 识别验证码
|
|
103
|
+
# 如果有前缀 "data:image/xxx;base64,", 需要去掉
|
|
104
|
+
if ',' in src:
|
|
105
|
+
base64_str = src.split(',')[1]
|
|
106
|
+
|
|
107
|
+
# 解码 base64 为字节流
|
|
108
|
+
img_bytes = base64.b64decode(base64_str)
|
|
109
|
+
captcha_code = self.__ocr.classification(img_bytes)
|
|
110
|
+
# 回填验证码
|
|
111
|
+
await captcha_code_input_element.fill(captcha_code)
|
|
112
|
+
# 再次点击登录按钮
|
|
113
|
+
await login_button_element.click()
|
|
114
|
+
try:
|
|
115
|
+
await page.wait_for_selector(".el-message.el-message--error", timeout=10000)
|
|
116
|
+
except TimeoutError:
|
|
117
|
+
# 获取userToken的cookie
|
|
118
|
+
cookies = await context.cookies()
|
|
119
|
+
for cookie in cookies:
|
|
120
|
+
if cookie["name"] == "userToken":
|
|
121
|
+
userToken = cookie["value"]
|
|
122
|
+
# 返回token
|
|
123
|
+
return userToken
|
|
124
|
+
# 防止验证码切换过快
|
|
125
|
+
await page.wait_for_timeout(2000)
|
|
126
|
+
login_count += 1
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise e
|
|
129
|
+
finally:
|
|
130
|
+
# 不管成功与否函数都要关闭
|
|
131
|
+
for close_obj in (page, context, browser):
|
|
132
|
+
await safe_close(close_obj)
|
|
133
|
+
|
|
134
|
+
async def __refresh_user_token(self):
|
|
135
|
+
"""
|
|
136
|
+
刷新token
|
|
137
|
+
:return:
|
|
138
|
+
"""
|
|
139
|
+
# 如果有登录任务正在运行
|
|
140
|
+
if self.__login_in_progress is not None:
|
|
141
|
+
# 已有协程在处理登录,我们等待它完成
|
|
142
|
+
await self.__login_in_progress
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
# 没有登录任务,当前协程负责登录
|
|
146
|
+
self.__login_in_progress = asyncio.get_event_loop().create_future()
|
|
147
|
+
# 检测锁,确保只有一个线程在登录
|
|
148
|
+
async with self.__login_lock:
|
|
149
|
+
try:
|
|
150
|
+
user_token = await self.__auto_login_4seller()
|
|
151
|
+
except Exception as e:
|
|
152
|
+
# 如果future没有被终结掉手动设置终结future
|
|
153
|
+
if not self.__login_in_progress.done():
|
|
154
|
+
self.__login_in_progress.set_exception(e)
|
|
155
|
+
logger.error(f'FourSeller自动登录失败 {e}')
|
|
156
|
+
raise Exception(f'FourSeller登录身份失效, 尝试自动登录失败 {e}')
|
|
157
|
+
else:
|
|
158
|
+
# 登录成功后重置token
|
|
159
|
+
self.user_token = user_token
|
|
160
|
+
# 清空原cookies
|
|
161
|
+
self.__async_client.cookies.clear()
|
|
162
|
+
# 重新设置客户端的cookie
|
|
163
|
+
self.__async_client.cookies.set('userToken', self.user_token, domain='www.4seller.com', path='/')
|
|
164
|
+
# 如果future没有被终结掉手动设置终结future,否则会抛出异常影响回调执行
|
|
165
|
+
if not self.__login_in_progress.done():
|
|
166
|
+
self.__login_in_progress.set_result(True)
|
|
167
|
+
# 回调函数可能会失败,但是不能影响其它协程操作
|
|
168
|
+
try:
|
|
169
|
+
await self.login_success_call_back(user_token)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
logger.error(f'FourSeller登录成功回调函数执行失败 {e}')
|
|
172
|
+
pass
|
|
173
|
+
finally:
|
|
174
|
+
# 提示登录任务完成
|
|
175
|
+
self.__login_in_progress = None
|
|
176
|
+
|
|
177
|
+
async def __request(self, url, method, retry_count=0, **kwargs):
|
|
178
|
+
"""
|
|
179
|
+
4seller基本请求
|
|
180
|
+
:param url: url路径
|
|
181
|
+
:param method: 请求方法
|
|
182
|
+
:param kwargs: 请求参数
|
|
183
|
+
:return:
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
async with self.__sem:
|
|
187
|
+
response = await self.__async_client.request(method, url, **kwargs)
|
|
188
|
+
except ReadTimeout:
|
|
189
|
+
raise Exception(f'FourSeller请求接口超时')
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.error(f'FourSeller请求接口失败{e}')
|
|
192
|
+
raise Exception(f'FourSeller请求接口失败{e}')
|
|
193
|
+
else:
|
|
194
|
+
try:
|
|
195
|
+
data = response.json()
|
|
196
|
+
except Exception as e:
|
|
197
|
+
logger.error(f'FourSeller请求,json序列化失败{e},原始数据{response.text}')
|
|
198
|
+
raise Exception(f'FourSeller请求,json序列化失败{e},原始数据{response.text}')
|
|
199
|
+
else:
|
|
200
|
+
if data.get('code') == ResponseCodeEnum.LOGIN_VALIDATION_FAILED:
|
|
201
|
+
# 刷新token
|
|
202
|
+
await self.__refresh_user_token()
|
|
203
|
+
if retry_count < 1:
|
|
204
|
+
# 最多重试一次
|
|
205
|
+
return await self.__request(url, method, 1, **kwargs)
|
|
206
|
+
else:
|
|
207
|
+
raise Exception(f'FourSeller登录成功后,再次请求登录验证失败, 请求接口{url},返回信息{data}')
|
|
208
|
+
return data
|
|
209
|
+
|
|
210
|
+
async def __get(self, url, **kwargs):
|
|
211
|
+
return await self.__request(url, 'GET', **kwargs)
|
|
212
|
+
|
|
213
|
+
async def __post(self, url, **kwargs):
|
|
214
|
+
return await self.__request(url, 'POST', **kwargs)
|
|
215
|
+
|
|
216
|
+
async def __put(self, url, **kwargs):
|
|
217
|
+
return await self.__request(url, 'PUT', **kwargs)
|
|
218
|
+
|
|
219
|
+
async def __delete(self, url, **kwargs):
|
|
220
|
+
return await self.__request(url, 'DELETE', **kwargs)
|
|
221
|
+
|
|
222
|
+
async def __order_page_api(self, query_params: FourSellerOrderQueryModel):
|
|
223
|
+
return await self.__post('/api/v2/order/page', json=query_params.model_dump(by_alias=True))
|
|
224
|
+
|
|
225
|
+
async def __order_page_history_api(self, query_params: FourSellerOrderQueryModel):
|
|
226
|
+
return await self.__post('/api/v2/order/history/page', json=query_params.model_dump(by_alias=True))
|
|
227
|
+
|
|
228
|
+
async def order_page(self, query_params: FourSellerOrderQueryModel) -> Page[FourSellerOrderVO]:
|
|
229
|
+
page_data = await self.__order_page_api(query_params)
|
|
230
|
+
# 能到这里说明登录验证什么的都没有问题,接口返回代码也是代表成功
|
|
231
|
+
if page_data.get('code') != ResponseCodeEnum.SUCCESS:
|
|
232
|
+
raise Exception(f'FourSeller请求接口失败, 请求接口{page_data}')
|
|
233
|
+
return parse_order(page_data.get('data'))
|
|
234
|
+
|
|
235
|
+
async def order_page_history(self, query_params: FourSellerOrderQueryModel) -> Page[FourSellerOrderVO]:
|
|
236
|
+
page_data = await self.__order_page_history_api(query_params)
|
|
237
|
+
# 能到这里说明登录验证什么的都没有问题,接口返回代码也是代表成功
|
|
238
|
+
if page_data.get('code') != ResponseCodeEnum.SUCCESS:
|
|
239
|
+
raise Exception(f'FourSeller拉取历史分页订单失败, 请求接口{page_data}')
|
|
240
|
+
return parse_order(page_data.get('data'))
|
|
241
|
+
|
|
242
|
+
async def list_order(self, query_params: FourSellerOrderQueryModel) -> list[FourSellerOrderVO]:
|
|
243
|
+
# 获取分页信息
|
|
244
|
+
order_list = list()
|
|
245
|
+
page = await self.order_page(query_params)
|
|
246
|
+
# 采用并发提高速度
|
|
247
|
+
page_order_task_list = list()
|
|
248
|
+
for page_number in range(1, page.total_page + 1):
|
|
249
|
+
page_order_task_list.append(
|
|
250
|
+
self.order_page(query_params.copy(update={"page_current": page_number}))
|
|
251
|
+
)
|
|
252
|
+
page_list = await asyncio.gather(*page_order_task_list)
|
|
253
|
+
# 所有的数据
|
|
254
|
+
for every_page in page_list:
|
|
255
|
+
# 插入数据
|
|
256
|
+
order_list.extend(every_page.records)
|
|
257
|
+
return order_list
|
|
258
|
+
|
|
259
|
+
async def list_history_order(self, query_params: FourSellerOrderQueryModel):
|
|
260
|
+
# 获取分页信息
|
|
261
|
+
order_history_list = list()
|
|
262
|
+
page = await self.order_page_history(query_params)
|
|
263
|
+
# 采用并发提高速度
|
|
264
|
+
page_order_task_list = list()
|
|
265
|
+
for page_number in range(1, page.total_page + 1):
|
|
266
|
+
page_order_task_list.append(self.order_page_history(
|
|
267
|
+
query_params.copy(update={"page_current": page_number})
|
|
268
|
+
))
|
|
269
|
+
page_list = await asyncio.gather(*page_order_task_list)
|
|
270
|
+
# 所有的数据
|
|
271
|
+
for every_page in page_list:
|
|
272
|
+
# 插入数据
|
|
273
|
+
order_history_list.extend(every_page.records)
|
|
274
|
+
return order_history_list
|
|
275
|
+
|
|
276
|
+
async def __aenter__(self):
|
|
277
|
+
return self
|
|
278
|
+
|
|
279
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
280
|
+
await self.__async_client.aclose()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
from ey_commerce_lib.four_seller.schemas.vo.order import FourSellerOrderVO
|
|
4
|
+
from ey_commerce_lib.model import Page
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def parse_order(data: dict) -> Page[FourSellerOrderVO]:
|
|
8
|
+
# 计算total_page
|
|
9
|
+
page_size = data.get('pageSize')
|
|
10
|
+
total = data.get('total')
|
|
11
|
+
page_number = data.get('pageCurrent')
|
|
12
|
+
total_page = math.ceil(total / page_size) if total > 0 else 0
|
|
13
|
+
return Page(
|
|
14
|
+
records=[FourSellerOrderVO(**record) for record in data.get('records')],
|
|
15
|
+
total=total,
|
|
16
|
+
page_size=page_size,
|
|
17
|
+
page_number=page_number,
|
|
18
|
+
total_page=total_page
|
|
19
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from pydantic import BaseModel, ConfigDict
|
|
2
|
+
|
|
3
|
+
from ey_commerce_lib.utils.str import to_camel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FourSellerOrderQueryModel(BaseModel):
|
|
7
|
+
# 页码
|
|
8
|
+
page_current: int = 1
|
|
9
|
+
# 每页条数
|
|
10
|
+
page_size: int = 100
|
|
11
|
+
# 平台
|
|
12
|
+
platform: str = ""
|
|
13
|
+
# 店铺id列表
|
|
14
|
+
shop_id_list: list[str] = []
|
|
15
|
+
# 仓库id 页面未知来源
|
|
16
|
+
warehouse_id: str = ""
|
|
17
|
+
# 物流id 例如: 13554
|
|
18
|
+
logistics_service_id: str = ""
|
|
19
|
+
# 物流服务id映射关系父子id,id连接起来 例如: ["470", "13554"]
|
|
20
|
+
logistics_service_map: list[str] = []
|
|
21
|
+
# 单品单数: onlyOne 全部包裹类型: "" 单品多数: gtOne 多品: multiple
|
|
22
|
+
item_type: str = ""
|
|
23
|
+
"""
|
|
24
|
+
打印状态 全部: "" 面单已打印: labelPrinted 面单未打印: labelUnPrinted 装箱已打印: packingPrinted 装箱未打印: packingUnPrinted
|
|
25
|
+
拣货单已打印: pickPrinted 拣货单未打印: pickUnPrinted
|
|
26
|
+
"""
|
|
27
|
+
print_status: str = ""
|
|
28
|
+
# 买家指定
|
|
29
|
+
shipping_method: list[str] = []
|
|
30
|
+
# 搜索类型 订单号: orderNo 物流跟踪号: trackNo 其余请参考页面
|
|
31
|
+
search_type: str = "orderNo"
|
|
32
|
+
# 搜索方式 模糊: like 精确: exact
|
|
33
|
+
search_method: str = "exact"
|
|
34
|
+
# 搜索内容
|
|
35
|
+
search_value: str = ""
|
|
36
|
+
# 商品总数最小
|
|
37
|
+
item_min: str = ""
|
|
38
|
+
# 商品总数最大
|
|
39
|
+
item_max: str = ""
|
|
40
|
+
# 重量最小
|
|
41
|
+
weight_min: str = ""
|
|
42
|
+
# 重量最大
|
|
43
|
+
weight_max: str = ""
|
|
44
|
+
# 重量单位 lb oz kg g
|
|
45
|
+
weight_unit: str = "lb"
|
|
46
|
+
# 到期时间 如果是数字就是小时 如果为空字符串是全部
|
|
47
|
+
expires_hour: str | int = ""
|
|
48
|
+
hours: str = ""
|
|
49
|
+
# 平台状态 待发货: Awaiting_Shipment 待揽收: Awaiting_Collection 其它参考页面
|
|
50
|
+
platform_status: str = ""
|
|
51
|
+
# 发货地址id 如果为空字符串就是没有选择
|
|
52
|
+
ship_from_address_id: str | int = ""
|
|
53
|
+
# 国家编码 具体参考筛选页面
|
|
54
|
+
country_code: str = ""
|
|
55
|
+
# 国家名称 具体参考筛选页面
|
|
56
|
+
country_name: str = ""
|
|
57
|
+
# 省份或者洲
|
|
58
|
+
state: str = ""
|
|
59
|
+
# 买家备注 空字符串未选 有:1 无: 0
|
|
60
|
+
buyer_note: str | int = ""
|
|
61
|
+
# 卖家备注 空字符串未选 有:1 无: 0
|
|
62
|
+
seller_note: str = ""
|
|
63
|
+
# 系统备注: 空字符串未选 有:1 无: 0
|
|
64
|
+
internal_note: str = ""
|
|
65
|
+
# 平台标记 PRE_SALE: Temu预售
|
|
66
|
+
platform_marker: str = ""
|
|
67
|
+
# 下单时间 这个时间比上海时间要晚7个小时
|
|
68
|
+
date_query_create_start_time: str = ""
|
|
69
|
+
date_query_create_end_time: str = ""
|
|
70
|
+
# 付款时间
|
|
71
|
+
date_query_paid_start_time: str = ""
|
|
72
|
+
date_query_paid_end_time: str = ""
|
|
73
|
+
# 发货时间
|
|
74
|
+
date_query_ship_start_time: str = ""
|
|
75
|
+
date_query_ship_end_time: str = ""
|
|
76
|
+
# 打印面单时间
|
|
77
|
+
date_query_print_label_start_time: str = ""
|
|
78
|
+
date_query_print_label_end_time: str = ""
|
|
79
|
+
# 取消订单时间
|
|
80
|
+
date_query_cancel_start_time: str = ""
|
|
81
|
+
date_query_cancel_end_time: str = ""
|
|
82
|
+
# 标签页 具体参考筛选页面
|
|
83
|
+
tag_id_list: list[str] = []
|
|
84
|
+
# 订单状态 全部: 空字符串 未付款: un_paid 已发货: shipped 待处理: to_ship 风控中: on_hold 处理中: in_process 已签收: delivered 参考页面
|
|
85
|
+
all_order_status: str = ""
|
|
86
|
+
# 订单状态: 全部: all 已发货: shipped
|
|
87
|
+
order_status: str = "all"
|
|
88
|
+
# 排序字段
|
|
89
|
+
order_by: str = "orderPaidTime"
|
|
90
|
+
# 排序方式 asc: 升序 desc: 降序
|
|
91
|
+
desc: str = "asc"
|
|
92
|
+
|
|
93
|
+
country: str = ""
|
|
94
|
+
note_query: str = ""
|
|
95
|
+
tag_query: str = ""
|
|
96
|
+
exception_type: str = ""
|
|
97
|
+
|
|
98
|
+
model_config = ConfigDict(
|
|
99
|
+
alias_generator=to_camel,
|
|
100
|
+
populate_by_name=True
|
|
101
|
+
)
|
|
File without changes
|