python-qdairlines-helper 0.4.3__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_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/METADATA +4 -4
- {python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/RECORD +14 -14
- qdairlines_helper/controller/book_payment.py +14 -15
- qdairlines_helper/controller/book_search.py +33 -30
- qdairlines_helper/controller/nhlms_cashdesk.py +13 -10
- qdairlines_helper/controller/order_verify.py +10 -1
- qdairlines_helper/controller/pay_success.py +5 -15
- qdairlines_helper/controller/user_login.py +5 -3
- qdairlines_helper/po/book_search_page.py +75 -4
- qdairlines_helper/po/nhlms_cash_desk_page.py +1 -1
- qdairlines_helper/po/order_verify_page.py +40 -1
- {python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/WHEEL +0 -0
- {python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/top_level.txt +0 -0
{python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_qdairlines_helper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: qdairlines helper python package
|
|
5
5
|
Author-email: ckf10000 <ckf10000@sina.com>
|
|
6
6
|
License: Apache License
|
|
@@ -210,9 +210,9 @@ Project-URL: Issues, https://github.com/ckf10000/python-qdairlines-helper/issues
|
|
|
210
210
|
Requires-Python: >=3.12
|
|
211
211
|
Description-Content-Type: text/markdown
|
|
212
212
|
License-File: LICENSE
|
|
213
|
-
Requires-Dist: python_playwright_helper>=0.4.
|
|
214
|
-
Requires-Dist: python_http_helper>=0.2.
|
|
215
|
-
Requires-Dist: flight_helper>=0.3.
|
|
213
|
+
Requires-Dist: python_playwright_helper>=0.4.3
|
|
214
|
+
Requires-Dist: python_http_helper>=0.2.2
|
|
215
|
+
Requires-Dist: flight_helper>=0.3.7
|
|
216
216
|
Dynamic: license-file
|
|
217
217
|
|
|
218
218
|
# python-qdairlines-helper
|
{python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/RECORD
RENAMED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
python_qdairlines_helper-0.
|
|
1
|
+
python_qdairlines_helper-0.6.0.dist-info/licenses/LICENSE,sha256=WtjCEwlcVzkh1ziO35P2qfVEkLjr87Flro7xlHz3CEY,11556
|
|
2
2
|
qdairlines_helper/__init__.py,sha256=bN1FqEK_jvefO_HU_8HFJpjuOzHTsuhmRe5KAzmraR4,479
|
|
3
3
|
qdairlines_helper/config/__init__.py,sha256=JjIRocePx3gwFNpdauuiwBU6B2Ug565xaHLEMu5Oo0I,479
|
|
4
4
|
qdairlines_helper/config/url_const.py,sha256=KpvvZEPOsJ7FQdJLp79VfUdUSRuItR4OYYCzl8wfKZI,2194
|
|
5
5
|
qdairlines_helper/controller/__init__.py,sha256=oyeciEBlDLNpqHblB0R-nXQG3HsI8L5mmlWulQ7Y38Q,482
|
|
6
6
|
qdairlines_helper/controller/add_passenger.py,sha256=qixajF_MRzmMZ9htAFeGCP3L27iV1Xk-jnfPQEP48s8,5155
|
|
7
|
-
qdairlines_helper/controller/book_payment.py,sha256=
|
|
8
|
-
qdairlines_helper/controller/book_search.py,sha256=
|
|
7
|
+
qdairlines_helper/controller/book_payment.py,sha256=yk1A99yHAjVcnXGBSzGanA6Cle4NXwjt6e8Qx4W2Xi0,6184
|
|
8
|
+
qdairlines_helper/controller/book_search.py,sha256=qSsjyn6BgJc5yP-mf2mNIDkEPsG_PTUffH4cEAZFsl8,9267
|
|
9
9
|
qdairlines_helper/controller/cash_pax_info.py,sha256=AZ09ekt-zaYYs3uUSclul014mbCZEF5BVIghOeIGT5s,2507
|
|
10
10
|
qdairlines_helper/controller/home.py,sha256=2r75pe7d16kZ6tkJZ3zM5ycV-DE5iy3njhpE0xf1EZw,1390
|
|
11
|
-
qdairlines_helper/controller/nhlms_cashdesk.py,sha256=
|
|
11
|
+
qdairlines_helper/controller/nhlms_cashdesk.py,sha256=Z5ApNGAzYWgBn43zcTqt42h1cFABLtfEDKWTIWSXsAA,4421
|
|
12
12
|
qdairlines_helper/controller/order_detail.py,sha256=hhL37PiozS1jALMk_eB0vg0eigW_Rcl_mf0l-QrW21o,4337
|
|
13
13
|
qdairlines_helper/controller/order_query.py,sha256=7U6S5xlT89UJHFslDbUT5VRgGlAoPQSOi4NRtEKK9xQ,1903
|
|
14
|
-
qdairlines_helper/controller/order_verify.py,sha256=
|
|
15
|
-
qdairlines_helper/controller/pay_success.py,sha256=
|
|
16
|
-
qdairlines_helper/controller/user_login.py,sha256=
|
|
14
|
+
qdairlines_helper/controller/order_verify.py,sha256=lx2YpDUoooCkU596IEz4kwyJsev-dHskBLHJ4v3d1Rg,2670
|
|
15
|
+
qdairlines_helper/controller/pay_success.py,sha256=J0yWSarIlce4cVXdqZyURWm-DwujTqg32al5JTe5flA,2837
|
|
16
|
+
qdairlines_helper/controller/user_login.py,sha256=CNuPIpGlkk5uU9ltL3DtTgC2SK6K32yA-K0Q-Kxe3MM,6267
|
|
17
17
|
qdairlines_helper/http/__init__.py,sha256=96AJPf2zgw_rm3Wv-boIBltWFgZH3Yo-fn5M9SRK280,489
|
|
18
18
|
qdairlines_helper/http/flight_order.py,sha256=7NTUA8xt6gwPBH7gdU3VjUoBa9LpPAvAEzkWguLPrNk,2066
|
|
19
19
|
qdairlines_helper/http/http_base.py,sha256=zWSgJfyMaekHyvV4lJD-KHiCX9BN3GGb8DperyVpRGQ,2492
|
|
@@ -21,16 +21,16 @@ qdairlines_helper/http/user_login.py,sha256=S_thG1RLXv5x5MQJmEG-oo38ReIF3_p17hVw
|
|
|
21
21
|
qdairlines_helper/po/__init__.py,sha256=HoouLJGLu_dI6IAKTVjBRraGHMcAUgntayTph-BUBAE,481
|
|
22
22
|
qdairlines_helper/po/add_passenger_page.py,sha256=no_QC4hwO7xuZXKLzakNlcWj2R06oVZdzKHvFHsBG5Y,7996
|
|
23
23
|
qdairlines_helper/po/air_order_page.py,sha256=0Ux0B2hUGKCoFLRpJqGHS2w690o0A2PDnvRf9-UKfJE,930
|
|
24
|
-
qdairlines_helper/po/book_search_page.py,sha256=
|
|
24
|
+
qdairlines_helper/po/book_search_page.py,sha256=Id4sqpmIQvgxTcESh-3dpCG5ccigkL6VrNLbZPf1gq0,14547
|
|
25
25
|
qdairlines_helper/po/cash_pax_info_page.py,sha256=yH-ZedYweby1x3Sw3JZX6a57-8PdtQHmNKqym7cn-Xo,3397
|
|
26
26
|
qdairlines_helper/po/home_page.py,sha256=KvoOZOYwKjlJjZOT522K_8ic3_2zaYOPWkuklUHVSlM,907
|
|
27
27
|
qdairlines_helper/po/login_page.py,sha256=SdWmkP_47GeosbFRcfvXLeQxxdM7b9UESvmajWq4-nA,2975
|
|
28
|
-
qdairlines_helper/po/nhlms_cash_desk_page.py,sha256=
|
|
29
|
-
qdairlines_helper/po/order_verify_page.py,sha256=
|
|
28
|
+
qdairlines_helper/po/nhlms_cash_desk_page.py,sha256=I2FdZvEfEiN9pbD6nZ7VquO1ZMg9zUJjVkStx9eat0M,6670
|
|
29
|
+
qdairlines_helper/po/order_verify_page.py,sha256=PlDZ-WbZBwV7pjjuQjXuMrYXAvys0Uas2FncXitsBLs,4978
|
|
30
30
|
qdairlines_helper/po/pay_success_page.py,sha256=XGddo8as8wf1kCCdvemqHIRWOPIPv1JG8iXEmpH4zck,1329
|
|
31
31
|
qdairlines_helper/utils/__init__.py,sha256=sti2S709puM2mQbQisk0KGq_YNjW6T4zz2vyYXonNB0,479
|
|
32
32
|
qdairlines_helper/utils/po_utils.py,sha256=74ZVyyxPmPk1tcGQoMauXhBE3qsZJej8urni1ch27io,2417
|
|
33
|
-
python_qdairlines_helper-0.
|
|
34
|
-
python_qdairlines_helper-0.
|
|
35
|
-
python_qdairlines_helper-0.
|
|
36
|
-
python_qdairlines_helper-0.
|
|
33
|
+
python_qdairlines_helper-0.6.0.dist-info/METADATA,sha256=WtDdd9E0T432X1X5uzk-HRnEt6U2s5OBVrVcV1nXEeE,14424
|
|
34
|
+
python_qdairlines_helper-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
35
|
+
python_qdairlines_helper-0.6.0.dist-info/top_level.txt,sha256=MRbQkBMdSG4f5mx2RWfu6aXoDSyM-2KwL-YFqNBdbng,18
|
|
36
|
+
python_qdairlines_helper-0.6.0.dist-info/RECORD,,
|
|
@@ -15,6 +15,7 @@ from playwright.async_api import Page
|
|
|
15
15
|
from typing import Any, List, Callable, Optional, Dict
|
|
16
16
|
from flight_helper.models.dto.passenger import PassengerDTO
|
|
17
17
|
from flight_helper.models.dto.itinerary import QueryItineraryRequestDTO
|
|
18
|
+
from qdairlines_helper.controller.pay_success import two_check_pay_success
|
|
18
19
|
from flight_helper.models.dto.booking import BookingInputDTO, OneWayBookingDTO
|
|
19
20
|
from qdairlines_helper.controller.book_search import open_book_search_page, book_search
|
|
20
21
|
from qdairlines_helper.controller.order_verify import load_order_verify_po, order_verify
|
|
@@ -23,14 +24,13 @@ from flight_helper.models.dto.payment import HFPaidAccountPaymentInputDTO, Payme
|
|
|
23
24
|
from qdairlines_helper.controller.add_passenger import add_passenger, load_add_passenger_po
|
|
24
25
|
from qdairlines_helper.controller.cash_pax_info import load_cash_pax_info_po, select_payment_channel
|
|
25
26
|
from qdairlines_helper.controller.nhlms_cashdesk import load_nhlms_cash_desk_po, hf_paid_account_payment
|
|
26
|
-
from qdairlines_helper.controller.pay_success import two_check_pay_success, check_hf_payment_is_success
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
async def book_payment_callback(
|
|
30
|
-
*, page: Page, logger: Logger,
|
|
30
|
+
*, page: Page, logger: Logger, pre_check_of_payment: Callable, book_input_dto: BookingInputDTO,
|
|
31
31
|
one_way_booking_dto: OneWayBookingDTO, passengers_dto: List[PassengerDTO], qdair_cookie: Dict[str, Any],
|
|
32
|
-
callback_get_proxy: Callable, cookie_jar: Optional[CookieJar] = None,
|
|
33
|
-
refresh_attempt: int = 3, retry: int = 0, enable_log: bool = True,
|
|
32
|
+
callback_get_proxy: Callable, qlv_cookie: Dict[str, Any], cookie_jar: Optional[CookieJar] = None,
|
|
33
|
+
timeout: float = 60.0, refresh_attempt: int = 3, retry: int = 0, enable_log: bool = True,
|
|
34
34
|
hf_paid_account_payment_dto: Optional[HFPaidAccountPaymentInputDTO] = None, **kwargs: Any
|
|
35
35
|
) -> PaymentResultDTO:
|
|
36
36
|
# 1. 打开预订搜索页面
|
|
@@ -66,7 +66,10 @@ async def book_payment_callback(
|
|
|
66
66
|
)
|
|
67
67
|
|
|
68
68
|
# 6. 校验订单
|
|
69
|
-
await order_verify(
|
|
69
|
+
await order_verify(
|
|
70
|
+
page=order_verify_po, logger=logger, refresh_attempt=refresh_attempt, timeout=timeout,
|
|
71
|
+
qdair_user_id=book_input_dto.book_login_user
|
|
72
|
+
)
|
|
70
73
|
logger.info(f"订单<{one_way_booking_dto.order_no}>,校验订单结束")
|
|
71
74
|
|
|
72
75
|
# 7. 加载支付类型页面对象
|
|
@@ -94,8 +97,8 @@ async def book_payment_callback(
|
|
|
94
97
|
# 10. 汇付天下操作支付
|
|
95
98
|
payment_result_dto = await hf_paid_account_payment(
|
|
96
99
|
page=nhlms_cash_desk_po, logger=logger, order_no=one_way_booking_dto.order_no, timeout=timeout,
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
hf_paid_account_payment_dto=hf_paid_account_payment_dto, pre_check_of_payment=pre_check_of_payment,
|
|
101
|
+
order_id=one_way_booking_dto.order_no, qlv_cookie=qlv_cookie
|
|
99
102
|
)
|
|
100
103
|
logger.info(f"订单<{one_way_booking_dto.order_no}>,汇付天下操作支付结束")
|
|
101
104
|
payment_result_dto.pre_order_no = pre_order_no
|
|
@@ -105,14 +108,10 @@ async def book_payment_callback(
|
|
|
105
108
|
storage_state=qdair_cookie.get("storage_state"), token=qdair_cookie.get("token"),
|
|
106
109
|
pre_order_no=pre_order_no, user_id=qdair_cookie.get("user_id"), headers=qdair_cookie.get("headers")
|
|
107
110
|
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
page=page, logger=logger, timeout=timeout, query_dto=query_dto, retry=retry, enable_log=enable_log,
|
|
113
|
-
callback_get_proxy=callback_get_proxy, cookie_jar=cookie_jar
|
|
114
|
-
)
|
|
115
|
-
await check_hf_payment_is_success(page=nhlms_cash_desk_po, logger=logger)
|
|
111
|
+
await two_check_pay_success(
|
|
112
|
+
page=nhlms_cash_desk_po, logger=logger, timeout=timeout, query_dto=query_dto, retry=retry,
|
|
113
|
+
enable_log=enable_log, callback_get_proxy=callback_get_proxy, cookie_jar=cookie_jar
|
|
114
|
+
)
|
|
116
115
|
return payment_result_dto
|
|
117
116
|
else:
|
|
118
117
|
raise PaymentChannelMissError()
|
|
@@ -9,15 +9,14 @@
|
|
|
9
9
|
# Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
|
-
import time
|
|
13
12
|
import asyncio
|
|
13
|
+
from time import time
|
|
14
14
|
from logging import Logger
|
|
15
|
-
from typing import Dict, Any, List
|
|
15
|
+
from typing import Dict, Any, List
|
|
16
16
|
from playwright.async_api import Page, Locator
|
|
17
17
|
import qdairlines_helper.config.url_const as url_const
|
|
18
18
|
from qdairlines_helper.po.book_search_page import BookSearchPage
|
|
19
19
|
from qdairlines_helper.utils.po_utils import get_ip_access_blocked_msg
|
|
20
|
-
from playwright.async_api import TimeoutError as PlaywrightTimeoutError
|
|
21
20
|
from flight_helper.models.dto.booking import OneWayBookingDTO, BookingInputDTO
|
|
22
21
|
from flight_helper.utils.exception_utils import NotEnoughTicketsError, ExcessiveProfitdError, ExcessiveLossesError, \
|
|
23
22
|
IPBlockError
|
|
@@ -39,26 +38,18 @@ async def open_book_search_page(
|
|
|
39
38
|
return book_search_po
|
|
40
39
|
|
|
41
40
|
|
|
42
|
-
async def _book_search_dialog_handle(
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
last_exception: Optional[Exception] = None
|
|
47
|
-
while time.time() < end_time:
|
|
41
|
+
async def _book_search_dialog_handle(*, logger: Logger, page: BookSearchPage, timeout: float = 20.0) -> None:
|
|
42
|
+
end_time = time() + timeout
|
|
43
|
+
while time() < end_time:
|
|
44
|
+
await page.handle_yidun_icon(logger=logger, timeout=1)
|
|
48
45
|
try:
|
|
49
|
-
# 2. 点击继续购票按钮
|
|
50
46
|
continue_book_btn = await page.get_reminder_dialog_continue_book_btn(timeout=1)
|
|
51
47
|
await continue_book_btn.click(button="left")
|
|
52
48
|
logger.info("航班预订查询页面,出现温馨提醒弹框,【继续购票】按钮点击完成")
|
|
49
|
+
return
|
|
53
50
|
except (Exception,):
|
|
54
51
|
pass
|
|
55
|
-
|
|
56
|
-
# 1.获取航班基本信息plane locator
|
|
57
|
-
flight_info_plane: Locator = await page.get_flight_info_plane(flight_no=flight_no, timeout=1)
|
|
58
|
-
return flight_info_plane
|
|
59
|
-
except (Exception,) as e:
|
|
60
|
-
last_exception = e
|
|
61
|
-
raise last_exception
|
|
52
|
+
await asyncio.sleep(0.1) # 快速轮询
|
|
62
53
|
|
|
63
54
|
|
|
64
55
|
async def book_search(
|
|
@@ -67,17 +58,31 @@ async def book_search(
|
|
|
67
58
|
) -> None:
|
|
68
59
|
# 1.搜索栏输入起飞城市
|
|
69
60
|
depart_city_input = await book_search_page.get_depart_city_input(timeout=timeout)
|
|
70
|
-
await depart_city_input.fill(value=one_way_booking_dto.dep_code)
|
|
71
|
-
await
|
|
72
|
-
|
|
61
|
+
# await depart_city_input.fill(value=one_way_booking_dto.dep_code)
|
|
62
|
+
await book_search_page.simulation_input_element(
|
|
63
|
+
locator=depart_city_input, text=one_way_booking_dto.dep_code, delay=200
|
|
64
|
+
)
|
|
65
|
+
await asyncio.sleep(delay=1)
|
|
66
|
+
# await depart_city_input.press("Enter")
|
|
67
|
+
depart_city_result = await book_search_page.get_depart_city_search_result(
|
|
68
|
+
depart_city=one_way_booking_dto.dep_city, timeout=timeout
|
|
69
|
+
)
|
|
70
|
+
await depart_city_result.click(button="left")
|
|
73
71
|
logger.info(
|
|
74
72
|
f"航班预订查询页面,搜索栏-起飞城市<{one_way_booking_dto.dep_city} {one_way_booking_dto.dep_code}>输入完成")
|
|
75
73
|
|
|
76
74
|
# 2.搜索栏输入抵达城市
|
|
77
75
|
arrive_city_input = await book_search_page.get_arrive_city_input(timeout=timeout)
|
|
78
|
-
await arrive_city_input.fill(value=one_way_booking_dto.arr_code)
|
|
79
|
-
await
|
|
80
|
-
|
|
76
|
+
# await arrive_city_input.fill(value=one_way_booking_dto.arr_code)
|
|
77
|
+
await book_search_page.simulation_input_element(
|
|
78
|
+
locator=arrive_city_input, text=one_way_booking_dto.arr_code, delay=200
|
|
79
|
+
)
|
|
80
|
+
await asyncio.sleep(delay=1)
|
|
81
|
+
# await arrive_city_input.press("Enter")
|
|
82
|
+
arrive_city_result = await book_search_page.get_arrive_city_search_result(
|
|
83
|
+
arrive_city=one_way_booking_dto.arr_city, timeout=timeout
|
|
84
|
+
)
|
|
85
|
+
await arrive_city_result.click(button="left")
|
|
81
86
|
logger.info(
|
|
82
87
|
f"航班预订查询页面,搜索栏-抵达城市<{one_way_booking_dto.arr_city} {one_way_booking_dto.arr_code}>输入完成")
|
|
83
88
|
|
|
@@ -94,14 +99,12 @@ async def book_search(
|
|
|
94
99
|
logger.info(f"航班预订查询页面,【查询机票】按钮点击完成")
|
|
95
100
|
|
|
96
101
|
# 5.点击查询后,再次处理是否有弹框,并航班基本信息plane locator
|
|
97
|
-
|
|
98
|
-
logger=logger, page=book_search_page, timeout=timeout, flight_no=one_way_booking_dto.flight_no
|
|
99
|
-
)
|
|
102
|
+
await _book_search_dialog_handle(logger=logger, page=book_search_page, timeout=timeout)
|
|
100
103
|
|
|
101
104
|
# 6.获取航班基本信息plane locator
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
flight_info_plane: Locator = await book_search_page.get_flight_info_plane(
|
|
106
|
+
flight_no=one_way_booking_dto.flight_no, timeout=timeout
|
|
107
|
+
)
|
|
105
108
|
|
|
106
109
|
# 8. 获取产品类型
|
|
107
110
|
flight_product_nav: Locator = await book_search_page.get_flight_product_nav(
|
|
@@ -120,7 +123,7 @@ async def book_search(
|
|
|
120
123
|
locator=flight_info_plane, flight_no=one_way_booking_dto.flight_no, logger=logger
|
|
121
124
|
)
|
|
122
125
|
# 取出需要预订的舱位产品
|
|
123
|
-
products = [x for x in products if x.get("cabin")]
|
|
126
|
+
products = [x for x in products if x.get("cabin") == one_way_booking_dto.cabin and not x.get("ticket_info")]
|
|
124
127
|
# 根据价格升序排序(默认)
|
|
125
128
|
products = sorted(products, key=lambda x: x["amounts"]["amount"])
|
|
126
129
|
# 9.1 判断是否存在该舱位
|
|
@@ -11,10 +11,9 @@
|
|
|
11
11
|
"""
|
|
12
12
|
import inspect
|
|
13
13
|
from logging import Logger
|
|
14
|
-
from typing import Callable
|
|
14
|
+
from typing import Callable, Dict, Any
|
|
15
15
|
from playwright.async_api import BrowserContext
|
|
16
16
|
import qdairlines_helper.config.url_const as url_const
|
|
17
|
-
from flight_helper.utils.exception_utils import DuplicatePaymentError
|
|
18
17
|
from qdairlines_helper.po.nhlms_cash_desk_page import NhlmsCashDeskPage
|
|
19
18
|
from playwright_helper.utils.browser_utils import switch_for_table_window
|
|
20
19
|
from flight_helper.models.dto.payment import HFPaidAccountPaymentInputDTO, PaymentResultDTO
|
|
@@ -37,8 +36,8 @@ async def load_nhlms_cash_desk_po(
|
|
|
37
36
|
|
|
38
37
|
|
|
39
38
|
async def hf_paid_account_payment(
|
|
40
|
-
*, page: NhlmsCashDeskPage, logger: Logger, order_no: int,
|
|
41
|
-
hf_paid_account_payment_dto: HFPaidAccountPaymentInputDTO, timeout: float = 60.0
|
|
39
|
+
*, page: NhlmsCashDeskPage, logger: Logger, order_no: int, order_id: int, pre_check_of_payment: Callable,
|
|
40
|
+
qlv_cookie: Dict[str, Any], hf_paid_account_payment_dto: HFPaidAccountPaymentInputDTO, timeout: float = 60.0
|
|
42
41
|
) -> PaymentResultDTO:
|
|
43
42
|
# 1. 获取收银台支付流水
|
|
44
43
|
pay_transaction = await page.get_order_transaction(timeout=timeout)
|
|
@@ -65,13 +64,17 @@ async def hf_paid_account_payment(
|
|
|
65
64
|
await password_input.fill(value=hf_paid_account_payment_dto.password)
|
|
66
65
|
logger.info(f"汇付天下收银台页面,交易密码<{hf_paid_account_payment_dto.password}>输入完成")
|
|
67
66
|
|
|
68
|
-
#
|
|
69
|
-
if inspect.iscoroutinefunction(
|
|
70
|
-
|
|
67
|
+
# 5.1. 前置检查,为何放在此处
|
|
68
|
+
if inspect.iscoroutinefunction(pre_check_of_payment):
|
|
69
|
+
await pre_check_of_payment(
|
|
70
|
+
order_id=order_id, logger=logger, cookie=qlv_cookie,
|
|
71
|
+
payment_channel=hf_paid_account_payment_dto.payment_type
|
|
72
|
+
)
|
|
71
73
|
else:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
pre_check_of_payment(
|
|
75
|
+
order_id=order_id, logger=logger, cookie=qlv_cookie,
|
|
76
|
+
payment_channel=hf_paid_account_payment_dto.payment_type
|
|
77
|
+
)
|
|
75
78
|
|
|
76
79
|
# 6. 点击【确认支付】
|
|
77
80
|
confirm_payment_btn = await page.get_confirm_payment_btn(timeout=timeout)
|
|
@@ -13,6 +13,7 @@ from logging import Logger
|
|
|
13
13
|
from playwright.async_api import Page
|
|
14
14
|
import qdairlines_helper.config.url_const as url_const
|
|
15
15
|
from flight_helper.utils.exception_utils import IPBlockError
|
|
16
|
+
from flight_helper.utils.exception_utils import OrderLimitError
|
|
16
17
|
from qdairlines_helper.po.order_verify_page import OrderVerifyPage
|
|
17
18
|
from qdairlines_helper.utils.po_utils import get_ip_access_blocked_msg
|
|
18
19
|
|
|
@@ -32,7 +33,7 @@ async def load_order_verify_po(
|
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
async def order_verify(
|
|
35
|
-
*, page: OrderVerifyPage, logger: Logger, refresh_attempt: int = 3, timeout: float = 60.0
|
|
36
|
+
*, page: OrderVerifyPage, logger: Logger, qdair_user_id: str, refresh_attempt: int = 3, timeout: float = 60.0
|
|
36
37
|
) -> None:
|
|
37
38
|
# 1. 先确认订单数据是否加载出来
|
|
38
39
|
await page.get_order_info_plane(timeout=timeout, refresh_attempt=refresh_attempt)
|
|
@@ -46,3 +47,11 @@ async def order_verify(
|
|
|
46
47
|
next_btn = await page.get_next_btn(timeout=timeout)
|
|
47
48
|
await next_btn.click(button="left")
|
|
48
49
|
logger.info("订单校验页面,【下一步】按钮点击完成")
|
|
50
|
+
|
|
51
|
+
# 4. 判断是否存在消息提示弹框
|
|
52
|
+
message: str = await page.get_message_box(logger=logger, timeout=5)
|
|
53
|
+
if message:
|
|
54
|
+
raise OrderLimitError(user_id=qdair_user_id, message=message)
|
|
55
|
+
|
|
56
|
+
# 5. 看看是否出现易盾校验码
|
|
57
|
+
await page.handle_yidun_icon(logger=logger, timeout=5)
|
|
@@ -37,18 +37,13 @@ async def load_pay_success_page(
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
async def two_check_pay_success(
|
|
40
|
-
*, page:
|
|
40
|
+
*, page: NhlmsCashDeskPage, logger: Logger, query_dto: QueryItineraryRequestDTO, callback_get_proxy: Callable,
|
|
41
41
|
cookie_jar: Optional[CookieJar] = None, timeout: float = 60.0, retry: int = 0, enable_log: bool = True
|
|
42
|
-
) ->
|
|
42
|
+
) -> None:
|
|
43
43
|
try:
|
|
44
44
|
# 1. 看支付成功页面是否加载完成
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
timeout=timeout
|
|
48
|
-
)
|
|
49
|
-
# 2. 检测页面的支付成功image是否存在
|
|
50
|
-
await pay_success_po.get_pay_success_image(timeout=timeout)
|
|
51
|
-
return True
|
|
45
|
+
await page.check_is_pay_success(logger=logger)
|
|
46
|
+
return
|
|
52
47
|
except Exception as e:
|
|
53
48
|
logger.error(e)
|
|
54
49
|
|
|
@@ -58,10 +53,5 @@ async def two_check_pay_success(
|
|
|
58
53
|
callback_get_proxy=callback_get_proxy, logger=logger
|
|
59
54
|
)
|
|
60
55
|
order_status = (order_detail.get("orderStatus", "")).upper()
|
|
61
|
-
if order_status not in ("TICKED",):
|
|
56
|
+
if order_status not in ("TICKED", "PAYED"):
|
|
62
57
|
raise PaymentFailedError(pre_order_no=query_dto.pre_order_no, order_status=order_status)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
async def check_hf_payment_is_success(*, page: NhlmsCashDeskPage, logger: Logger) -> None:
|
|
66
|
-
# 1. 看支付成功页面是否加载完成
|
|
67
|
-
await page.check_is_pay_success(logger=logger)
|
|
@@ -106,9 +106,10 @@ async def get_user_info(
|
|
|
106
106
|
) -> Dict[str, Any]:
|
|
107
107
|
user_login = UserLogin(http_request_dto=http_request_dto, cookie_jar=cookie_jar)
|
|
108
108
|
last_user_info: Dict[str, Any] = dict()
|
|
109
|
-
|
|
109
|
+
count = 2
|
|
110
|
+
for index in range(count):
|
|
110
111
|
proxy = None
|
|
111
|
-
is_end: bool = True if index + 1 ==
|
|
112
|
+
is_end: bool = True if index + 1 == count else False
|
|
112
113
|
if index != 0:
|
|
113
114
|
if inspect.iscoroutinefunction(callback_get_proxy):
|
|
114
115
|
proxy = await callback_get_proxy(logger=logger)
|
|
@@ -121,7 +122,8 @@ async def get_user_info(
|
|
|
121
122
|
await user_login.http_client.close()
|
|
122
123
|
logger.info("获取青岛航空官网的用户详情数据成功")
|
|
123
124
|
break
|
|
124
|
-
except (Exception,):
|
|
125
|
+
except (Exception,) as e:
|
|
126
|
+
logger.error(e)
|
|
125
127
|
pass
|
|
126
128
|
if isinstance(last_user_info, dict) and last_user_info.get("result") and last_user_info.get("code") == 1:
|
|
127
129
|
return last_user_info.get("result")
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"""
|
|
12
12
|
import re
|
|
13
13
|
from logging import Logger
|
|
14
|
-
from typing import Optional, Dict, Any, List
|
|
15
14
|
from playwright.async_api import Page, Locator
|
|
16
15
|
from playwright_helper.libs.base_po import BasePo
|
|
16
|
+
from typing import Optional, Dict, Any, List, Literal
|
|
17
17
|
import qdairlines_helper.config.url_const as url_const
|
|
18
18
|
from flight_helper.utils.exception_utils import ProductTypeError
|
|
19
19
|
from playwright_helper.utils.type_utils import convert_order_amount_text, safe_convert_advanced
|
|
@@ -37,14 +37,21 @@ class BookSearchPage(BasePo):
|
|
|
37
37
|
selector: str = '//strong[contains(text(), "没有在飞航班")]'
|
|
38
38
|
return await self.get_locator(selector=selector, timeout=timeout)
|
|
39
39
|
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _get_reminder_dialog_selector() -> str:
|
|
42
|
+
return '//div[@class="el-dialog__body"]//button[contains(@class, "search_btn")]'
|
|
43
|
+
|
|
44
|
+
async def is_exist_reminder_dialog(self) -> bool:
|
|
45
|
+
# 使用 count() 快速判断是否存在(不会因找不到而抛异常)
|
|
46
|
+
return await self.__page.locator(selector=self._get_reminder_dialog_selector()).count() > 0
|
|
47
|
+
|
|
40
48
|
async def get_reminder_dialog_continue_book_btn(self, timeout: float = 5.0) -> Locator:
|
|
41
49
|
"""
|
|
42
50
|
获取预订搜索页的温馨提醒弹框中的【继续购票】按钮
|
|
43
51
|
:param timeout: 超时时间(秒)
|
|
44
52
|
:return: (是否存在, 错误信息|元素对象)
|
|
45
53
|
"""
|
|
46
|
-
|
|
47
|
-
return await self.get_locator(selector=selector, timeout=timeout)
|
|
54
|
+
return await self.get_locator(selector=self._get_reminder_dialog_selector(), timeout=timeout)
|
|
48
55
|
|
|
49
56
|
async def get_depart_city_input(self, timeout: float = 5.0) -> Locator:
|
|
50
57
|
"""
|
|
@@ -55,6 +62,22 @@ class BookSearchPage(BasePo):
|
|
|
55
62
|
selector: str = '//div[contains(@class, "flight_search_form")]//input[@id="orig"]'
|
|
56
63
|
return await self.get_locator(selector=selector, timeout=timeout)
|
|
57
64
|
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _get_city_search_result_selector(city_name: str, endpoint: Literal["orig", "dest"]) -> str:
|
|
67
|
+
return f'xpath=//div[@name="{endpoint}" and @class="vcp-select"]//span[contains(text(), "{city_name}")]/..'
|
|
68
|
+
|
|
69
|
+
async def get_depart_city_search_result(self, depart_city: str, timeout: float = 5.0) -> Locator:
|
|
70
|
+
"""
|
|
71
|
+
获取预订搜索页搜索栏的起飞城市搜索结果
|
|
72
|
+
:param depart_city: 起飞城市
|
|
73
|
+
:param timeout: 超时时间(秒)
|
|
74
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
75
|
+
"""
|
|
76
|
+
if depart_city == "晋江":
|
|
77
|
+
depart_city = "泉州"
|
|
78
|
+
selector = self._get_city_search_result_selector(city_name=depart_city, endpoint="orig")
|
|
79
|
+
return await self.get_locator(selector=selector, timeout=timeout, strict=False)
|
|
80
|
+
|
|
58
81
|
async def get_arrive_city_input(self, timeout: float = 5.0) -> Locator:
|
|
59
82
|
"""
|
|
60
83
|
获取预订搜索页搜索栏的抵达城市输入框
|
|
@@ -64,6 +87,18 @@ class BookSearchPage(BasePo):
|
|
|
64
87
|
selector: str = '//div[contains(@class, "flight_search_form")]//input[@id="dest"]'
|
|
65
88
|
return await self.get_locator(selector=selector, timeout=timeout)
|
|
66
89
|
|
|
90
|
+
async def get_arrive_city_search_result(self, arrive_city: str, timeout: float = 5.0) -> Locator:
|
|
91
|
+
"""
|
|
92
|
+
获取预订搜索页搜索栏的抵达城市搜索结果
|
|
93
|
+
:param arrive_city: 抵达城市
|
|
94
|
+
:param timeout: 超时时间(秒)
|
|
95
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
96
|
+
"""
|
|
97
|
+
if arrive_city == "晋江":
|
|
98
|
+
arrive_city = "泉州"
|
|
99
|
+
selector = self._get_city_search_result_selector(city_name=arrive_city, endpoint="dest")
|
|
100
|
+
return await self.get_locator(selector=selector, timeout=timeout, strict=False)
|
|
101
|
+
|
|
67
102
|
async def get_depart_date_input(self, timeout: float = 5.0) -> Locator:
|
|
68
103
|
"""
|
|
69
104
|
获取预订搜索页搜索栏的起飞日期输入框
|
|
@@ -144,8 +179,10 @@ class BookSearchPage(BasePo):
|
|
|
144
179
|
amounts: Dict[str, Any] = await self._get_flight_product_price(locator=locator, timeout=timeout)
|
|
145
180
|
seats_status: int = await self._get_flight_product_seats_status(locator=locator, timeout=timeout)
|
|
146
181
|
cabin = await self._get_flight_product_cabin(locator=locator, timeout=timeout)
|
|
182
|
+
ticket_info: str = await self._get_flight_product_ticket_info(locator=locator, timeout=timeout)
|
|
147
183
|
flight_products.append(dict(
|
|
148
|
-
amounts=amounts, cabin=cabin, seats_status=seats_status, booking_btn=booking_btn
|
|
184
|
+
amounts=amounts, cabin=cabin, seats_status=seats_status, booking_btn=booking_btn,
|
|
185
|
+
ticket_info=ticket_info
|
|
149
186
|
))
|
|
150
187
|
except (PlaywrightError, PlaywrightTimeoutError, EnvironmentError, RuntimeError, Exception) as e:
|
|
151
188
|
logger.error(e)
|
|
@@ -189,6 +226,20 @@ class BookSearchPage(BasePo):
|
|
|
189
226
|
selector: str = 'xpath=(.//td[@class="ticket_num"]/div/span)[1]'
|
|
190
227
|
return await self.get_sub_locator(locator=locator, selector=selector, timeout=timeout)
|
|
191
228
|
|
|
229
|
+
async def _get_flight_product_ticket_info(self, locator: Locator, timeout: float = 5.0) -> Optional[str]:
|
|
230
|
+
"""
|
|
231
|
+
获取预订搜索页航班内容栏航班产品下的单据信息
|
|
232
|
+
:param timeout: 超时时间(秒)
|
|
233
|
+
:param locator: flight_product Locator 对象
|
|
234
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
235
|
+
"""
|
|
236
|
+
try:
|
|
237
|
+
selector: str = 'xpath=.//td[@class="ticket_info"]//span[contains(@class, "text-left")]'
|
|
238
|
+
locator = await self.get_sub_locator(locator=locator, selector=selector, timeout=timeout)
|
|
239
|
+
return (await locator.inner_text()).strip()
|
|
240
|
+
except (Exception,):
|
|
241
|
+
return
|
|
242
|
+
|
|
192
243
|
async def _get_flight_product_seats_status(self, locator: Locator, timeout: float = 5.0) -> int:
|
|
193
244
|
"""
|
|
194
245
|
获取预订搜索页航班内容栏航班产品下的余票信息
|
|
@@ -205,3 +256,23 @@ class BookSearchPage(BasePo):
|
|
|
205
256
|
return safe_convert_advanced(value=match.group().strip())
|
|
206
257
|
else:
|
|
207
258
|
return 999999
|
|
259
|
+
|
|
260
|
+
async def handle_yidun_icon(self, logger: Logger, timeout: float = 20.0) -> None:
|
|
261
|
+
"""
|
|
262
|
+
获取搜索页面中的易盾校验弹框
|
|
263
|
+
:param logger: 日志对象
|
|
264
|
+
:param timeout: 超时时间(秒)
|
|
265
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
266
|
+
"""
|
|
267
|
+
selector: str = 'xpath=//div[@class="yidun_intelli-tips"]//div[@class="yidun_intelli-icon"]'
|
|
268
|
+
try:
|
|
269
|
+
icon: Locator = await self.get_locator(selector=selector, timeout=timeout, strict=False)
|
|
270
|
+
await icon.click(button="left")
|
|
271
|
+
logger.info("预订搜索页面,易盾校验框【易盾icon】点击完成")
|
|
272
|
+
btn: Locator = await self.get_locator(
|
|
273
|
+
selector='//div[@class="el-dialog__body"]//button[@type="button"]', timeout=3, strict=False
|
|
274
|
+
)
|
|
275
|
+
await btn.click(button="left")
|
|
276
|
+
logger.info("预订搜索页面,易盾校验框【确认】点击完成")
|
|
277
|
+
except (Exception,):
|
|
278
|
+
pass
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
# Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
|
-
from
|
|
12
|
+
from logging import Logger
|
|
13
|
+
from typing import Optional, List
|
|
13
14
|
from playwright.async_api import Page, Locator
|
|
14
15
|
from playwright_helper.libs.base_po import BasePo
|
|
15
16
|
import qdairlines_helper.config.url_const as url_const
|
|
@@ -24,6 +25,44 @@ class OrderVerifyPage(BasePo):
|
|
|
24
25
|
super().__init__(page, url or url_const.order_verify_url)
|
|
25
26
|
self.__page = page
|
|
26
27
|
|
|
28
|
+
async def handle_yidun_icon(self, logger: Logger, timeout: float = 20.0) -> None:
|
|
29
|
+
"""
|
|
30
|
+
获取订单校验页中的易盾校验弹框
|
|
31
|
+
:param logger: 日志对象
|
|
32
|
+
:param timeout: 超时时间(秒)
|
|
33
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
34
|
+
"""
|
|
35
|
+
selector: str = 'xpath=//div[@class="yidun_intelli-tips"]//div[@class="yidun_intelli-icon"]'
|
|
36
|
+
try:
|
|
37
|
+
locator: Locator = await self.get_locator(selector=selector, timeout=timeout, strict=False)
|
|
38
|
+
await locator.click(button="left")
|
|
39
|
+
logger.info("订单校验页面,易盾校验框【易盾icon】点击完成")
|
|
40
|
+
except (Exception,):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
async def get_message_box(self, logger: Logger, timeout: float = 20.0) -> Optional[str]:
|
|
44
|
+
"""
|
|
45
|
+
获取订单校验页中的提示消息弹框内容
|
|
46
|
+
内容可能是:1. 您的账户订单数已达最大上限,请前往青岛航空官方微信小程序进行预订。
|
|
47
|
+
2. 请前往青岛航空官方微信小程序进行预订。
|
|
48
|
+
3. 抱歉,证件号码为:450511197305180615已经创建订单,订单号OW20260117B1280813,请勿重复占座
|
|
49
|
+
:param logger: 日志对象
|
|
50
|
+
:param timeout: 超时时间(秒)
|
|
51
|
+
:return: (是否存在, 错误信息|元素对象)
|
|
52
|
+
"""
|
|
53
|
+
selectors: List[str] = [
|
|
54
|
+
'xpath=//div[@class="el-message-box"]//div[@class="el-message-box__message"]/p',
|
|
55
|
+
'xpath=//div[@class="layui-layer-content"]'
|
|
56
|
+
]
|
|
57
|
+
for selector in selectors:
|
|
58
|
+
try:
|
|
59
|
+
locator: Locator = await self.get_locator(selector=selector, timeout=timeout, strict=False)
|
|
60
|
+
text: str = (await locator.inner_text()).strip()
|
|
61
|
+
logger.info(f"订单校验页面,提示框消息内容【{text}】获取完成")
|
|
62
|
+
return text
|
|
63
|
+
except (Exception,) as e:
|
|
64
|
+
logger.error(e)
|
|
65
|
+
|
|
27
66
|
async def get_order_info_plane(self, timeout: float = 5.0, refresh_attempt: int = 3) -> Locator:
|
|
28
67
|
"""
|
|
29
68
|
获取订单校验页中的订单信息版面,注意这里有个小坑,经常出现已经进入了该页面,但是订单信息没有加载出来,需要不断尝试刷新页面
|
|
File without changes
|
|
File without changes
|
{python_qdairlines_helper-0.4.3.dist-info → python_qdairlines_helper-0.6.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|