python-qdairlines-helper 0.0.6__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.
Files changed (34) hide show
  1. python_qdairlines_helper-0.0.6.dist-info/METADATA +247 -0
  2. python_qdairlines_helper-0.0.6.dist-info/RECORD +34 -0
  3. python_qdairlines_helper-0.0.6.dist-info/WHEEL +5 -0
  4. python_qdairlines_helper-0.0.6.dist-info/licenses/LICENSE +201 -0
  5. python_qdairlines_helper-0.0.6.dist-info/top_level.txt +1 -0
  6. qdairlines_helper/__init__.py +11 -0
  7. qdairlines_helper/config/__init__.py +11 -0
  8. qdairlines_helper/config/url_const.py +37 -0
  9. qdairlines_helper/controller/__init__.py +11 -0
  10. qdairlines_helper/controller/add_passenger.py +92 -0
  11. qdairlines_helper/controller/book_payment.py +83 -0
  12. qdairlines_helper/controller/book_search.py +114 -0
  13. qdairlines_helper/controller/cash_pax_info.py +47 -0
  14. qdairlines_helper/controller/home.py +28 -0
  15. qdairlines_helper/controller/nhlms_cashdesk.py +74 -0
  16. qdairlines_helper/controller/order_detail.py +53 -0
  17. qdairlines_helper/controller/order_query.py +41 -0
  18. qdairlines_helper/controller/order_verify.py +43 -0
  19. qdairlines_helper/controller/user_login.py +91 -0
  20. qdairlines_helper/http/__init__.py +11 -0
  21. qdairlines_helper/http/flight_order.py +86 -0
  22. qdairlines_helper/po/__init__.py +11 -0
  23. qdairlines_helper/po/add_passenger_page.py +155 -0
  24. qdairlines_helper/po/air_order_page.py +24 -0
  25. qdairlines_helper/po/book_search_page.py +189 -0
  26. qdairlines_helper/po/cash_pax_info_page.py +74 -0
  27. qdairlines_helper/po/home_page.py +24 -0
  28. qdairlines_helper/po/login_page.py +70 -0
  29. qdairlines_helper/po/nhlms_cash_desk_page.py +94 -0
  30. qdairlines_helper/po/order_verify_page.py +62 -0
  31. qdairlines_helper/utils/__init__.py +11 -0
  32. qdairlines_helper/utils/exception_utils.py +17 -0
  33. qdairlines_helper/utils/log_utils.py +14 -0
  34. qdairlines_helper/utils/po_utils.py +50 -0
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: home_page.py
6
+ # Description: 首页页面对象
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/04
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+ from typing import Optional
13
+ from playwright.async_api import Page
14
+ from playwright_helper.libs.base_po import BasePo
15
+ import qdairlines_helper.config.url_const as url_const
16
+
17
+
18
+ class HomePage(BasePo):
19
+ url: str = url_const.home_url
20
+ __page: Page
21
+
22
+ def __init__(self, page: Page, url: Optional[str] = None) -> None:
23
+ super().__init__(page, url or url_const.home_url)
24
+ self.__page = page
@@ -0,0 +1,70 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: login_page.py
6
+ # Description: 登录页面对象
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/04
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+ from typing import Optional
13
+ from playwright.async_api import Page, Locator
14
+ from playwright_helper.libs.base_po import BasePo
15
+ import qdairlines_helper.config.url_const as url_const
16
+
17
+
18
+ class LoginPage(BasePo):
19
+ url: str = url_const.login_url
20
+ __page: Page
21
+
22
+ def __init__(self, page: Page, url: Optional[str] = None) -> None:
23
+ super().__init__(page, url or url_const.login_url)
24
+ self.__page = page
25
+
26
+ async def get_login_username_input(self, timeout: float = 5.0) -> Locator:
27
+ """
28
+ 获取登录页面的会员卡号输入框
29
+ :param timeout: 超时时间(秒)
30
+ :return: (是否存在, 错误信息|元素对象)
31
+ """
32
+ selector: str = '//input[@id="username"]'
33
+ return await self.get_locator(selector=selector, timeout=timeout)
34
+
35
+ async def get_login_password_input(self, timeout: float = 5.0) -> Locator:
36
+ """
37
+ 获取登录页面的密码输入框
38
+ :param timeout: 超时时间(秒)
39
+ :return: (是否存在, 错误信息|元素对象)
40
+ """
41
+ selector: str = '//input[@id="password"]'
42
+ return await self.get_locator(selector=selector, timeout=timeout)
43
+
44
+ async def get_check_logo_icon(self, timeout: float = 5.0) -> Locator:
45
+ """
46
+ 获取易盾校验logo图标
47
+ :param timeout: 超时时间(秒)
48
+ :return: (是否存在, 错误信息|元素对象)
49
+ """
50
+ selector: str = '//div[@class="yidun_intelli-icon"]'
51
+ return await self.get_locator(selector=selector, timeout=timeout)
52
+
53
+ async def get_check_pass_text(self, timeout: float = 5.0) -> str:
54
+ """
55
+ 点击易盾校验logo图标后,获取验证成功的文案
56
+ :param timeout: 超时时间(秒)
57
+ :return: (是否存在, 错误信息|元素对象)
58
+ """
59
+ selector: str = '//span[@class="yidun_tips__text yidun-fallback__tip" and contains(text(), "验证成功")]'
60
+ locator = await self.get_locator(selector=selector, timeout=timeout)
61
+ return (await locator.inner_text()).strip()
62
+
63
+ async def get_login_btn(self, timeout: float = 5.0) -> Locator:
64
+ """
65
+ 获取登录按钮
66
+ :param timeout: 超时时间(秒)
67
+ :return: (是否存在, 错误信息|元素对象)
68
+ """
69
+ selector: str = '//button[@class="search_btn"]'
70
+ return await self.get_locator(selector=selector, timeout=timeout)
@@ -0,0 +1,94 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: nhlms_cash_desk_page.py
6
+ # Description: 汇付天下收银台页面对象
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/05
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+ from typing import Optional, Any
13
+ from playwright.async_api import Page, Locator
14
+ from playwright_helper.libs.base_po import BasePo
15
+ import qdairlines_helper.config.url_const as url_const
16
+ from playwright_helper.utils.type_utils import safe_convert_advanced
17
+
18
+
19
+ class NhlmsCashDeskPage(BasePo):
20
+ url: str = url_const.nhlms_cashdesk_url
21
+ __page: Page
22
+
23
+ def __init__(self, page: Page, url: Optional[str] = None) -> None:
24
+ super().__init__(page, url or url_const.nhlms_cashdesk_url)
25
+ self.__page = page
26
+
27
+ async def get_order_amount(self, timeout: float = 5.0) -> Any:
28
+ """
29
+ 获取汇付天下收银台页的支付金额
30
+ :param timeout: 超时时间(秒)
31
+ :return: (是否存在, 错误信息|元素对象)
32
+ """
33
+ selector: str = 'xpath=//div[@class="form-text-info recharge"]'
34
+ locator: Locator = await self.get_locator(selector=selector, timeout=timeout)
35
+ return safe_convert_advanced((await locator.inner_text()).strip())
36
+
37
+ async def get_order_transaction(self, timeout: float = 5.0) -> str:
38
+ """
39
+ 获取汇付天下收银台页的订单流水
40
+ :param timeout: 超时时间(秒)
41
+ :return: (是否存在, 错误信息|元素对象)
42
+ """
43
+ selector: str = 'xpath=//div[@class="mer-info"]//span[contains(text(), "订单号")]//span'
44
+ locator: Locator = await self.get_locator(selector=selector, timeout=timeout)
45
+ return (await locator.inner_text()).strip()
46
+
47
+ async def get_payment_type_tab(self, payment_type: str = "付款账户支付", timeout: float = 5.0) -> Locator:
48
+ """
49
+ 获取汇付天下收银台页的付款方式tab
50
+ :param payment_type: 付款方式,包括:付款账户支付|保理账户支付|个人网银|企业网银|快捷支付
51
+ :param timeout: 超时时间(秒)
52
+ :return: (是否存在, 错误信息|元素对象)
53
+ """
54
+ if payment_type == '付款账户支付':
55
+ value = "ap"
56
+ elif payment_type == "保理账户支付":
57
+ value = "bl"
58
+ elif payment_type == "个人网银":
59
+ value = "user"
60
+ elif payment_type == "企业网银":
61
+ value = "mer"
62
+ elif payment_type == "快捷支付":
63
+ value = "fp"
64
+ else:
65
+ raise EnvironmentError(f"汇付天下收银台暂不支持<{payment_type}>的付款方式")
66
+ selector: str = f'//div[@class="content-bottom network-recharge"]//li[@value="{value}"]'
67
+ return await self.get_locator(selector=selector, timeout=timeout)
68
+
69
+ async def get_username_input(self, timeout: float = 5.0) -> Locator:
70
+ """
71
+ 获取汇付天下收银台页的操作员号输入框
72
+ :param timeout: 超时时间(秒)
73
+ :return: (是否存在, 错误信息|元素对象)
74
+ """
75
+ selector: str = 'xpath=//div[@class="content-bottom network-recharge"]//input[@id="userId"]'
76
+ return await self.get_locator(selector=selector, timeout=timeout)
77
+
78
+ async def get_password_input(self, timeout: float = 5.0) -> Locator:
79
+ """
80
+ 获取汇付天下收银台页的密码输入框
81
+ :param timeout: 超时时间(秒)
82
+ :return: (是否存在, 错误信息|元素对象)
83
+ """
84
+ selector: str = 'xpath=//div[@class="content-bottom network-recharge"]//input[@id="usrPw"]'
85
+ return await self.get_locator(selector=selector, timeout=timeout)
86
+
87
+ async def get_confirm_payment_btn(self, timeout: float = 5.0) -> Locator:
88
+ """
89
+ 获取汇付天下收银台页中的【确认支付】按钮
90
+ :param timeout: 超时时间(秒)
91
+ :return: (是否存在, 错误信息|元素对象)
92
+ """
93
+ selector: str = 'xpath=//div[@class="content-bottom network-recharge"]//button[@id="submitBtn"]'
94
+ return await self.get_locator(selector=selector, timeout=timeout)
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: order_verify_page.py
6
+ # Description: 订单校验页面对象
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/04
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+ from typing import Optional
13
+ from playwright.async_api import Page, Locator
14
+ from playwright_helper.libs.base_po import BasePo
15
+ from qdairlines_helper.utils.log_utils import logger
16
+ import qdairlines_helper.config.url_const as url_const
17
+ from playwright.async_api import Error as PlaywrightError, TimeoutError as PlaywrightTimeoutError
18
+
19
+
20
+ class OrderVerifyPage(BasePo):
21
+ url: str = url_const.order_verify_url
22
+ __page: Page
23
+
24
+ def __init__(self, page: Page, url: Optional[str] = None) -> None:
25
+ super().__init__(page, url or url_const.order_verify_url)
26
+ self.__page = page
27
+
28
+ async def get_order_info_plane(self, timeout: float = 5.0, refresh_attempt: int = 3) -> Locator:
29
+ """
30
+ 获取订单校验页中的订单信息版面,注意这里有个小坑,经常出现已经进入了该页面,但是订单信息没有加载出来,需要不断尝试刷新页面
31
+ :param timeout: 超时时间(秒)
32
+ :param refresh_attempt: 尝试刷新次数
33
+ :return: (是否存在, 错误信息|元素对象)
34
+ """
35
+ attempt = 0
36
+ selector: str = 'xpath=//div[@class="order_info"]'
37
+ while attempt <= refresh_attempt:
38
+ try:
39
+ await self.__page.reload(timeout=timeout)
40
+ return await self.get_locator(selector=selector, timeout=timeout)
41
+ except (PlaywrightError, PlaywrightTimeoutError, EnvironmentError, RuntimeError, Exception) as e:
42
+ attempt += 1
43
+ logger.error(f"订单校验页面,第<{attempt}>次尝试刷新加载订单数据失败,原因:{e}")
44
+ raise RuntimeError("订单校验页面中的订单信息加载出现异常,可能是网络拥塞或者用户被风控")
45
+
46
+ async def get_agree_checkbox(self, timeout: float = 5.0) -> Locator:
47
+ """
48
+ 获取订单校验页中的【同意条款】单选框
49
+ :param timeout: 超时时间(秒)
50
+ :return: (是否存在, 错误信息|元素对象)
51
+ """
52
+ selector: str = 'xpath=//span[@class="el-checkbox__inner"]'
53
+ return await self.get_locator(selector=selector, timeout=timeout)
54
+
55
+ async def get_next_btn(self, timeout: float = 5.0) -> Locator:
56
+ """
57
+ 获取添加乘客页中的【下一步】按钮
58
+ :param timeout: 超时时间(秒)
59
+ :return: (是否存在, 错误信息|元素对象)
60
+ """
61
+ selector: str = 'xpath=//span[contains(text(), "立即购票")]'
62
+ return await self.get_locator(selector=selector, timeout=timeout)
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: __init__.py
6
+ # Description: 工具包
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/04
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: exception_utils.py
6
+ # Description: 异常工具模块
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/07
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+
13
+
14
+ class DuplicatePaymentError(Exception):
15
+ def __init__(self, order_id: int):
16
+ self.order_id = order_id
17
+ super().__init__(f"订单<{order_id}>重复支付错误")
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: log_utils.py
6
+ # Description: 日志模块
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/04
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+ from logging import getLogger
13
+
14
+ logger = getLogger("root")
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ # ---------------------------------------------------------------------------------------------------------
4
+ # ProjectName: python-qdairlines-helper
5
+ # FileName: po_utils.py
6
+ # Description: 页面工具模块
7
+ # Author: ASUS
8
+ # CreateDate: 2026/01/05
9
+ # Copyright ©2011-2026. Hunan xxxxxxx Company limited. All rights reserved.
10
+ # ---------------------------------------------------------------------------------------------------------
11
+ """
12
+ from typing import Optional, List, Dict, Any
13
+ import qdairlines_helper.config.url_const as url_const
14
+ from playwright.async_api import Page, Locator, Error as PlaywrightError, TimeoutError as PlaywrightTimeoutError
15
+
16
+
17
+ async def get_ip_access_blocked_msg(page: Page, timeout: float = 60.0) -> Optional[str]:
18
+ selector: str = '//h3[contains(text(), "您的IP")]'
19
+ locator: Locator = page.locator(selector=selector)
20
+ try:
21
+ await locator.first.wait_for(state='visible', timeout=timeout * 1000)
22
+ return (await locator.inner_text()).strip()
23
+ except (PlaywrightTimeoutError, PlaywrightError, Exception):
24
+ return
25
+
26
+
27
+ def parse_user_info_from_page_response(network_logs: List[Dict[str, Any]]) -> dict:
28
+ user_info = dict()
29
+ for network_log in network_logs:
30
+ try:
31
+ if network_log.get('type') == 'response':
32
+ if url_const.auth_form_api_url in (network_log.get("url") or ""):
33
+ body = network_log.get("body")
34
+ result = body.get('result')
35
+ user_info["access_token"] = result.get('access_token')
36
+ user_info["expires"] = result.get('expires_in')
37
+ elif url_const.login_after_api_url in (network_log.get("url") or ""):
38
+ body = network_log.get("body")
39
+ result = body.get('result')
40
+ user_info["user_id"] = result.get('userId')
41
+ user_info["id"] = result.get('id')
42
+ elif network_log.get('type') == 'request':
43
+ if url_const.login_after_api_url in (network_log.get("url") or ""):
44
+ headers = network_log.get('headers')
45
+ headers.pop("referer", None)
46
+ headers.pop("timestamp", None)
47
+ user_info["headers"] = headers
48
+ except (Exception,):
49
+ pass
50
+ return user_info