python-qlv-helper 0.2.0__py3-none-any.whl → 0.6.0__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.
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/METADATA +3 -2
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/RECORD +17 -14
- qlv_helper/config/__init__.py +11 -0
- qlv_helper/config/custom_exception.py +35 -0
- qlv_helper/config/url_const.py +13 -0
- qlv_helper/controller/order_detail.py +412 -4
- qlv_helper/controller/order_table.py +17 -1
- qlv_helper/http/order_page.py +143 -42
- qlv_helper/http/order_table_page.py +30 -4
- qlv_helper/po/domestic_activity_order_page.py +2 -4
- qlv_helper/po/login_page.py +1 -1
- qlv_helper/po/main_page.py +3 -4
- qlv_helper/po/order_detail_page.py +275 -0
- qlv_helper/po/wechat_auth_page.py +1 -1
- qlv_helper/po/base_po.py +0 -40
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/WHEEL +0 -0
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {python_qlv_helper-0.2.0.dist-info → python_qlv_helper-0.6.0.dist-info}/top_level.txt +0 -0
qlv_helper/http/order_page.py
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
12
|
import re
|
|
13
|
+
import json
|
|
13
14
|
import aiohttp
|
|
14
15
|
from datetime import datetime
|
|
16
|
+
from urllib.parse import quote
|
|
15
17
|
from bs4 import BeautifulSoup, Tag
|
|
16
18
|
from collections import OrderedDict
|
|
17
19
|
from typing import Dict, Any, Optional, List
|
|
@@ -22,8 +24,8 @@ from qlv_helper.utils.type_utils import get_key_by_index, get_value_by_index, sa
|
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
async def get_order_page_html(
|
|
25
|
-
order_id: int, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
|
|
26
|
-
cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
|
|
27
|
+
*, order_id: int, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
|
|
28
|
+
enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
|
|
27
29
|
) -> Dict[str, Any]:
|
|
28
30
|
order_http_client = HttpClientFactory(
|
|
29
31
|
protocol=protocol if protocol == "http" else "https",
|
|
@@ -41,6 +43,47 @@ async def get_order_page_html(
|
|
|
41
43
|
)
|
|
42
44
|
|
|
43
45
|
|
|
46
|
+
async def fill_procurement_info_with_http(
|
|
47
|
+
*, order_id: int, qlv_domain: str, amount: float, pre_order_id: str, platform_user_id: str, user_password: str,
|
|
48
|
+
passengers: List[str], fids: str, pids: List[str], transaction_id: str, qlv_protocol: str = "http",
|
|
49
|
+
retry: int = 1, timeout: int = 5, enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None,
|
|
50
|
+
playwright_state: Dict[str, Any] = None, data_list: Optional[List[Dict[str, Any]]] = None
|
|
51
|
+
) -> Dict[str, Any]:
|
|
52
|
+
client = HttpClientFactory(
|
|
53
|
+
protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, enable_log=enable_log, retry=retry,
|
|
54
|
+
cookie_jar=cookie_jar or aiohttp.CookieJar(),
|
|
55
|
+
playwright_state=playwright_state
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
headers = {
|
|
59
|
+
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
60
|
+
"Referer": f"{qlv_protocol}://{qlv_domain}/OrderProcessing/NewTicket/{order_id}?&r={datetime.now().strftime("%Y%m%d%H%M%S")}",
|
|
61
|
+
}
|
|
62
|
+
if data_list:
|
|
63
|
+
data = data_list
|
|
64
|
+
else:
|
|
65
|
+
remark = f"{platform_user_id}/{user_password}"
|
|
66
|
+
pName = "," + ",".join(passengers) + ","
|
|
67
|
+
pids = ",".join(pids)
|
|
68
|
+
data = [
|
|
69
|
+
{"tradingDat": datetime.now().strftime("%Y-%m-%d %H:%M"), "outTktPF": "G航司官网", "outTktLoginCode": "",
|
|
70
|
+
"typeName": "VCC", "accountID": "8", "accountName": "VCC", "transactionAmount": f"{amount}",
|
|
71
|
+
"mainCheckNumber": "", "airCoOrderID": f"{pre_order_id}", "QuotaResultAmount": "0.00",
|
|
72
|
+
"remark": f"{quote(remark)}", "flightIdx": ",1,", "pName": f"{pName}", "orderID": f"{order_id}",
|
|
73
|
+
"businessTypeName": "机票", "tradingItems": "机票支出", "actualAmount": 0, "pType": "成人",
|
|
74
|
+
"fids": f"{fids}", "pids": f"{pids}", "iscandel": "true", "isbatch": "false",
|
|
75
|
+
"MainCheckNumberValus": f"{transaction_id}",
|
|
76
|
+
"OfficeNo": "", "PriceStdActual": "0.00", "ReturnAmount": "0.0000", "OffsetReturnAmount": "0.00",
|
|
77
|
+
"profitRemark": "", "preSaleType": "", "ErrorType": "", "OutTktPFTypeID": "34", "OutTicketAccount": "",
|
|
78
|
+
"OutTicketAccountID": "", "OutTicketPWD": "", "OutTicketTel": "", "OutTicketPNR": ""}
|
|
79
|
+
]
|
|
80
|
+
data = f"list={json.dumps(data)}&isPayAll=true&delTransactionids=&OutTicketLossType&OutTicketLossRemark="
|
|
81
|
+
return await client.request(
|
|
82
|
+
method="POST", url="/OrderProcessing/PurchaseInfoSave",
|
|
83
|
+
headers=headers, is_end=True, data=data.encode("utf-8")
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
44
87
|
def order_info_static_headers() -> OrderedDict[str, str]:
|
|
45
88
|
return OrderedDict([
|
|
46
89
|
("receipted_ota", "OTA实收"), # 0
|
|
@@ -55,40 +98,73 @@ def order_info_static_headers() -> OrderedDict[str, str]:
|
|
|
55
98
|
|
|
56
99
|
def parser_order_info(html: str) -> Dict[str, Any]:
|
|
57
100
|
soup = BeautifulSoup(html, "html.parser")
|
|
58
|
-
# 找到目标
|
|
59
|
-
|
|
60
|
-
if not
|
|
101
|
+
# 找到目标 table_border
|
|
102
|
+
table_border = soup.find("table", class_="table no_border")
|
|
103
|
+
if not table_border:
|
|
104
|
+
return {}
|
|
105
|
+
|
|
106
|
+
table_flight = soup.find("table", class_="info_flight")
|
|
107
|
+
if not table_border:
|
|
61
108
|
return {}
|
|
62
109
|
|
|
63
110
|
# 所有 td
|
|
64
|
-
|
|
111
|
+
tds_border = table_border.find_all("td")
|
|
65
112
|
result = {}
|
|
66
113
|
|
|
67
|
-
for td in
|
|
68
|
-
text = td.get_text(strip=True)
|
|
69
|
-
# 如果 td 内没有冒号、也不是按钮,跳过
|
|
70
|
-
if ":" not in text:
|
|
71
|
-
continue
|
|
72
|
-
# 按 ":" 进行分割
|
|
114
|
+
for td in tds_border:
|
|
73
115
|
try:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
116
|
+
text = td.get_text(strip=True)
|
|
117
|
+
# 如果 td 内没有冒号、也不是按钮,跳过
|
|
118
|
+
if ":" not in text:
|
|
119
|
+
continue
|
|
120
|
+
# 按 ":" 进行分割
|
|
121
|
+
try:
|
|
122
|
+
key, value = text.split(":", 1)
|
|
123
|
+
except (Exception, ValueError):
|
|
124
|
+
continue
|
|
125
|
+
# 去掉换行符、空格
|
|
126
|
+
key = key.strip()
|
|
127
|
+
value = value.strip()
|
|
128
|
+
|
|
129
|
+
# 如果 value 为空,尝试取 <b> 或其他控件内文本
|
|
130
|
+
if not value:
|
|
131
|
+
b = td.find("b")
|
|
132
|
+
if b:
|
|
133
|
+
value = b.get_text(strip=True)
|
|
134
|
+
|
|
135
|
+
# 去掉尾部不需要的空格
|
|
136
|
+
value = value.replace("\u00a0", "").strip()
|
|
137
|
+
value = value[1:] if isinstance(value, str) and value.startswith("[") else value
|
|
138
|
+
value = value[:-1] if isinstance(value, str) and value.endswith("]") else value
|
|
139
|
+
result[key] = safe_convert_advanced(value)
|
|
140
|
+
except (Exception,):
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
# 所有 td
|
|
144
|
+
tds_flight = table_flight.find_all("td")
|
|
145
|
+
|
|
146
|
+
for td in tds_flight:
|
|
147
|
+
try:
|
|
148
|
+
dat_dep = td.find("span", class_="DatDep")
|
|
149
|
+
if dat_dep:
|
|
150
|
+
text = dat_dep.get_text(strip=True)
|
|
151
|
+
result["dat_dep"] = text + ":00"
|
|
152
|
+
city_dep = td.find("span", class_="CityDep")
|
|
153
|
+
if city_dep:
|
|
154
|
+
text = city_dep.get_text(strip=True)
|
|
155
|
+
city_dep = text.split("【")[0].strip()
|
|
156
|
+
result["city_dep"] = city_dep
|
|
157
|
+
dat_arr = td.find("p", class_="DatArr")
|
|
158
|
+
if dat_arr:
|
|
159
|
+
text = dat_arr.get_text(strip=True)
|
|
160
|
+
result["dat_arr"] = text + ":00"
|
|
161
|
+
city_arr = td.find("span", class_="CityArr")
|
|
162
|
+
if city_arr:
|
|
163
|
+
text = city_arr.get_text(strip=True)
|
|
164
|
+
city_arr = text.split("【")[0].strip()
|
|
165
|
+
result["city_arr"] = city_arr
|
|
166
|
+
except (Exception,):
|
|
167
|
+
pass
|
|
92
168
|
return convert_cn_to_en(data=result, header_map=order_info_static_headers())
|
|
93
169
|
|
|
94
170
|
|
|
@@ -121,6 +197,8 @@ def flight_extend_headers() -> OrderedDict[str, str]:
|
|
|
121
197
|
("id_valid_dat", " 证件有效期"), # 9
|
|
122
198
|
("code_dep", " 起飞机场"), # 10
|
|
123
199
|
("code_arr", " 抵达机场"), # 11
|
|
200
|
+
("pid", "乘客ID"), # 12
|
|
201
|
+
("fid", "航段ID"), # 13
|
|
124
202
|
])
|
|
125
203
|
|
|
126
204
|
|
|
@@ -236,7 +314,20 @@ def parse_order_flight_table_passenger_info(raw: Tag, headers: OrderedDict[str,
|
|
|
236
314
|
nationality = guobies[0].get_text(strip=True) if len(guobies) > 0 else ""
|
|
237
315
|
issue_country = guobies[1].get_text(strip=True) if len(guobies) > 1 else ""
|
|
238
316
|
|
|
239
|
-
|
|
317
|
+
a1 = raw.find("a", id=lambda x: x and x.startswith("IDNo_"))
|
|
318
|
+
a2 = raw.find("a", id=lambda x: x and x.startswith("detrni_"))
|
|
319
|
+
a3 = raw.find("a", id=lambda x: x and x.startswith("detrnif_"))
|
|
320
|
+
pid = None
|
|
321
|
+
if a1:
|
|
322
|
+
full_id = a1["id"] # IDNo_279778
|
|
323
|
+
pid = full_id.split("_")[-1]
|
|
324
|
+
elif a2:
|
|
325
|
+
full_id = a2["id"] # detrni_279778
|
|
326
|
+
pid = full_id.split("_")[-1]
|
|
327
|
+
elif a3:
|
|
328
|
+
full_id = a3["id"] # detrnif_279778
|
|
329
|
+
pid = full_id.split("_")[-1]
|
|
330
|
+
result = {
|
|
240
331
|
get_key_by_index(index=0, ordered_dict=headers): name, # 姓名
|
|
241
332
|
get_key_by_index(index=1, ordered_dict=headers): ptype, # 类型: 成人/儿童
|
|
242
333
|
get_key_by_index(index=2, ordered_dict=headers): id_type, # 证件类型
|
|
@@ -246,8 +337,12 @@ def parse_order_flight_table_passenger_info(raw: Tag, headers: OrderedDict[str,
|
|
|
246
337
|
get_key_by_index(index=6, ordered_dict=headers): sex, # 性别
|
|
247
338
|
get_key_by_index(index=7, ordered_dict=headers): nationality, # 国籍
|
|
248
339
|
get_key_by_index(index=8, ordered_dict=headers): issue_country, # 签发国
|
|
249
|
-
get_key_by_index(index=9, ordered_dict=headers): id_valid # 证件有效期
|
|
340
|
+
get_key_by_index(index=9, ordered_dict=headers): id_valid, # 证件有效期
|
|
250
341
|
}
|
|
342
|
+
if pid is not None:
|
|
343
|
+
# 乘客ID
|
|
344
|
+
result[get_key_by_index(index=12, ordered_dict=headers)] = pid
|
|
345
|
+
return result
|
|
251
346
|
|
|
252
347
|
|
|
253
348
|
def parse_order_flight_table_row(
|
|
@@ -255,8 +350,7 @@ def parse_order_flight_table_row(
|
|
|
255
350
|
) -> Dict[str, Any]:
|
|
256
351
|
"""解析航班表每一行的数据"""
|
|
257
352
|
tds = tr.find_all("td", recursive=False)
|
|
258
|
-
values = {}
|
|
259
|
-
|
|
353
|
+
values = {get_key_by_index(index=12, ordered_dict=extend_headers): tr["pid"]}
|
|
260
354
|
for idx, td in enumerate(tds):
|
|
261
355
|
if idx >= len(headers):
|
|
262
356
|
continue
|
|
@@ -270,6 +364,14 @@ def parse_order_flight_table_row(
|
|
|
270
364
|
else:
|
|
271
365
|
raw = clean_order_flight_table(html=td)
|
|
272
366
|
if "行程" in value:
|
|
367
|
+
fid = ""
|
|
368
|
+
input_tag = td.find('input', {'name': 'fid'})
|
|
369
|
+
fid_key = get_key_by_index(index=13, ordered_dict=extend_headers)
|
|
370
|
+
if input_tag:
|
|
371
|
+
match = re.search(r'\d+', input_tag.get('value'))
|
|
372
|
+
if match:
|
|
373
|
+
fid = match.group()
|
|
374
|
+
values[fid_key] = fid
|
|
273
375
|
code_dep_key = get_key_by_index(index=10, ordered_dict=extend_headers)
|
|
274
376
|
code_arr_key = get_key_by_index(index=11, ordered_dict=extend_headers)
|
|
275
377
|
raw_slice = raw.split("-")
|
|
@@ -280,7 +382,6 @@ def parse_order_flight_table_row(
|
|
|
280
382
|
values[key] = raw
|
|
281
383
|
else:
|
|
282
384
|
values[key] = safe_convert_advanced(raw)
|
|
283
|
-
|
|
284
385
|
return values
|
|
285
386
|
|
|
286
387
|
|
|
@@ -293,6 +394,8 @@ def extract_structured_table_data(table: Tag) -> List[Optional[Dict[str, Any]]]:
|
|
|
293
394
|
for tr in table.find_all("tr")[1:]: # 跳过表头
|
|
294
395
|
rows.append(parse_order_flight_table_row(tr=tr, headers=headers, extend_headers=extend))
|
|
295
396
|
|
|
397
|
+
if rows:
|
|
398
|
+
rows = list({i["id_no"]: i for i in sorted(rows, key=lambda x: bool(x.get("fid")))}.values())
|
|
296
399
|
return rows
|
|
297
400
|
|
|
298
401
|
|
|
@@ -300,14 +403,12 @@ def parser_order_flight_table(html: str) -> List[Optional[Dict[str, Any]]]:
|
|
|
300
403
|
"""解析航班表"""
|
|
301
404
|
soup = BeautifulSoup(html, 'html.parser')
|
|
302
405
|
# 三个主要的order_sort div
|
|
303
|
-
|
|
304
|
-
|
|
406
|
+
table_sections = soup.find_all('table', class_='table table_border table_center')
|
|
407
|
+
table = table_sections[2] if len(table_sections) > 2 else Tag(name="")
|
|
305
408
|
results = list()
|
|
306
409
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
table_data
|
|
310
|
-
if table_data:
|
|
311
|
-
results.extend(table_data)
|
|
410
|
+
table_data = extract_structured_table_data(table)
|
|
411
|
+
if table_data:
|
|
412
|
+
results.extend(table_data)
|
|
312
413
|
|
|
313
414
|
return results
|
|
@@ -81,10 +81,34 @@ async def get_domestic_ticket_outed_page_html(
|
|
|
81
81
|
json_data["PageCountFormPage"] = pages
|
|
82
82
|
return await order_http_client.request(method="post", url=url, is_end=is_end, json_data=json_data)
|
|
83
83
|
|
|
84
|
+
async def get_domestic_unticketed_order_page_html(
|
|
85
|
+
domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5, enable_log: bool = True,
|
|
86
|
+
cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None, current_page: int = 1,
|
|
87
|
+
pages: int = 1, order_http_client: HttpClientFactory = None, is_end: bool = True
|
|
88
|
+
) -> Dict[str, Any]:
|
|
89
|
+
if not order_http_client:
|
|
90
|
+
order_http_client = HttpClientFactory(
|
|
91
|
+
protocol=protocol if protocol == "http" else "https",
|
|
92
|
+
domain=domain,
|
|
93
|
+
timeout=timeout,
|
|
94
|
+
retry=retry,
|
|
95
|
+
enable_log=enable_log,
|
|
96
|
+
cookie_jar=cookie_jar,
|
|
97
|
+
playwright_state=playwright_state
|
|
98
|
+
)
|
|
99
|
+
url = "/OrderList/GuoNei_TicketOutUndo"
|
|
100
|
+
if current_page == 1:
|
|
101
|
+
return await order_http_client.request(method="get", url=url, is_end=is_end)
|
|
102
|
+
else:
|
|
103
|
+
json_data = deepcopy(kwargs)
|
|
104
|
+
json_data["JumpPageFromPage"] = current_page
|
|
105
|
+
json_data["PageCountFormPage"] = pages
|
|
106
|
+
return await order_http_client.request(method="post", url=url, is_end=is_end, json_data=json_data)
|
|
107
|
+
|
|
84
108
|
|
|
85
109
|
def processing_static_headers() -> OrderedDict[str, str]:
|
|
86
110
|
return OrderedDict([
|
|
87
|
-
("
|
|
111
|
+
("source_name", "来源"), # 0
|
|
88
112
|
("id", "订单号"), # 1
|
|
89
113
|
("raw_order_no", "平台订单号"), # 2
|
|
90
114
|
("adult_pnr", "成人PNR"), # 3
|
|
@@ -104,7 +128,7 @@ def processing_static_headers() -> OrderedDict[str, str]:
|
|
|
104
128
|
|
|
105
129
|
def completed_static_headers() -> OrderedDict[str, str]:
|
|
106
130
|
return OrderedDict([
|
|
107
|
-
("
|
|
131
|
+
("source_name", "来源"), # 0
|
|
108
132
|
("id", "订单号"), # 1
|
|
109
133
|
("raw_order_no", "平台订单号"), # 2
|
|
110
134
|
("adult_pnr", "成人PNR"), # 3
|
|
@@ -274,8 +298,10 @@ def parse_order_row(cells: ResultSet[Tag], headers: OrderedDict, extend_headers:
|
|
|
274
298
|
total_child_key = get_key_by_index(ordered_dict=extend_headers, index=7)
|
|
275
299
|
order_data[total_adult_key] = safe_convert_advanced(cell_text_slice[1])
|
|
276
300
|
order_data[total_child_key] = safe_convert_advanced(cell_text_slice[2])
|
|
277
|
-
|
|
301
|
+
elif header_key == "receipted":
|
|
278
302
|
order_data[header_key] = safe_convert_advanced(cell_text)
|
|
303
|
+
else:
|
|
304
|
+
order_data[header_key] = cell_text
|
|
279
305
|
|
|
280
306
|
# 提取特殊属性
|
|
281
307
|
extract_special_attributes(cells=cells, order_data=order_data, header=headers)
|
|
@@ -317,7 +343,7 @@ def parse_order_table(html: str, table_state: str = "proccessing") -> List[Optio
|
|
|
317
343
|
cells = row.find_all(['td', 'th'])
|
|
318
344
|
|
|
319
345
|
order_data = parse_order_row(cells, headers=headers, extend_headers=extend, row_index=i)
|
|
320
|
-
if order_data:
|
|
346
|
+
if order_data and order_data.get("id") is not None:
|
|
321
347
|
orders.append(order_data)
|
|
322
348
|
|
|
323
349
|
return orders
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
12
|
from bs4 import BeautifulSoup
|
|
13
|
-
from qlv_helper.po.base_po import BasePo
|
|
14
13
|
from typing import Tuple, Union, List, Any, Dict
|
|
14
|
+
from playwright_helper.libs.base_po import BasePo
|
|
15
15
|
from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator
|
|
16
16
|
|
|
17
17
|
|
|
@@ -105,13 +105,11 @@ class DomesticActivityOrderPage(BasePo):
|
|
|
105
105
|
# 解析当前页的数据
|
|
106
106
|
rows = self.parse_tbody_rows(tbody_html=tbody_html, headers=headers)
|
|
107
107
|
all_rows.extend(rows)
|
|
108
|
-
print(all_rows)
|
|
109
108
|
|
|
110
109
|
# --- 2. 获取当前页数与总页数 ---
|
|
111
110
|
current_page: int = int(await self.__page.locator('//label[@id="Lab_PageIndex"][1]').inner_text())
|
|
112
111
|
pages: int = int(await self.__page.locator('//label[@id="Lab_PageCount"][1]').inner_text())
|
|
113
112
|
|
|
114
|
-
print(current_page, pages)
|
|
115
113
|
# --- 3. 如果到最后一页,退出 ---
|
|
116
114
|
if current_page >= pages:
|
|
117
115
|
break
|
|
@@ -122,7 +120,7 @@ class DomesticActivityOrderPage(BasePo):
|
|
|
122
120
|
# 等待页面刷新(简单但稳)
|
|
123
121
|
await self.__page.wait_for_timeout(timeout=refresh_wait_time)
|
|
124
122
|
except PlaywrightTimeoutError:
|
|
125
|
-
all_rows.append(dict(error_message=f"元素 '{self.__table_selector}' 未在 {
|
|
123
|
+
all_rows.append(dict(error_message=f"元素 '{self.__table_selector}' 未在 {refresh_wait_time} 秒内找到"))
|
|
126
124
|
except Exception as e:
|
|
127
125
|
all_rows.append(dict(error_message=f"检查元素时发生错误: {str(e)}"))
|
|
128
126
|
|
qlv_helper/po/login_page.py
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
12
|
from typing import Tuple, Union
|
|
13
|
-
from
|
|
13
|
+
from playwright_helper.libs.base_po import BasePo
|
|
14
14
|
from qlv_helper.utils.ocr_helper import get_image_text
|
|
15
15
|
from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator
|
|
16
16
|
|
qlv_helper/po/main_page.py
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
12
|
from typing import Tuple, Union
|
|
13
|
-
from
|
|
14
|
-
from qlv_helper.utils.ocr_helper import get_image_text
|
|
13
|
+
from playwright_helper.libs.base_po import BasePo
|
|
15
14
|
from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator
|
|
16
15
|
|
|
17
16
|
|
|
@@ -42,7 +41,7 @@ class MainPage(BasePo):
|
|
|
42
41
|
except Exception as e:
|
|
43
42
|
return False, f"检查元素时发生错误: {str(e)}"
|
|
44
43
|
|
|
45
|
-
async def get_level1_menu_order_checkout(self) -> Tuple[bool, Union[Locator, str]]:
|
|
44
|
+
async def get_level1_menu_order_checkout(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
|
|
46
45
|
selector: str = "//span[contains(normalize-space(), '订单出票')]"
|
|
47
46
|
try:
|
|
48
47
|
locator = self.__page.locator(selector)
|
|
@@ -56,7 +55,7 @@ class MainPage(BasePo):
|
|
|
56
55
|
except Exception as e:
|
|
57
56
|
return False, f"检查元素时发生错误: {str(e)}"
|
|
58
57
|
|
|
59
|
-
async def get_level2_menu_order_checkout(self) -> Tuple[bool, Union[Locator, str]]:
|
|
58
|
+
async def get_level2_menu_order_checkout(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
|
|
60
59
|
selector: str = "//a[@menuname='国内活动订单']"
|
|
61
60
|
try:
|
|
62
61
|
locator = self.__page.locator(selector)
|