python-qlv-helper 0.7.2__py3-none-any.whl → 0.9.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python_qlv_helper
3
- Version: 0.7.2
3
+ Version: 0.9.3
4
4
  Summary: qlv helper python package
5
5
  Author-email: ckf10000 <ckf10000@sina.com>
6
6
  License: Apache License
@@ -216,10 +216,10 @@ Requires-Dist: ddddocr==1.5.6; python_version >= "3.12"
216
216
  Requires-Dist: aiohttp==3.13.2; python_version >= "3.12"
217
217
  Requires-Dist: beautifulsoup4==4.14.2; python_version >= "3.12"
218
218
  Requires-Dist: airtest==1.3.6; python_version >= "3.12"
219
- Requires-Dist: python_http_helper>=0.2.0; python_version >= "3.12"
220
- Requires-Dist: python_playwright_helper>=0.3.0; python_version >= "3.12"
221
- Requires-Dist: python_ocr_helper>=0.0.1; python_version >= "3.12"
222
- Requires-Dist: flight_helper>=0.2.9
219
+ Requires-Dist: python_http_helper>=0.2.1; python_version >= "3.12"
220
+ Requires-Dist: python_playwright_helper>=0.4.3; python_version >= "3.12"
221
+ Requires-Dist: python_ocr_helper>=0.0.2; python_version >= "3.12"
222
+ Requires-Dist: flight_helper>=0.3.6
223
223
  Dynamic: license-file
224
224
 
225
225
  # qlv-helper
@@ -1,4 +1,4 @@
1
- python_qlv_helper-0.7.2.dist-info/licenses/LICENSE,sha256=WtjCEwlcVzkh1ziO35P2qfVEkLjr87Flro7xlHz3CEY,11556
1
+ python_qlv_helper-0.9.3.dist-info/licenses/LICENSE,sha256=WtjCEwlcVzkh1ziO35P2qfVEkLjr87Flro7xlHz3CEY,11556
2
2
  qlv_helper/__init__.py,sha256=5DCc5JhfdsgtIuFWgkxPOW5VVKZ8RPikQLGIuyZX6_Y,465
3
3
  qlv_helper/config/__init__.py,sha256=0pKLgui-sC6yMNBBuuTTLkUGhPybiJQSTKTbi66alvg,465
4
4
  qlv_helper/config/custom_exception.py,sha256=uYme0iseQt_dP-Y6-hZ_H2OA2OQR0Kp5PmmZvhEZlEc,805
@@ -6,19 +6,19 @@ qlv_helper/config/url_const.py,sha256=EoLHOtlO3Ob2WSMMv6TBrl7eJCfJCB844dwmD_k86B
6
6
  qlv_helper/controller/__init__.py,sha256=cOJA0xMIytv17oICzPYqWLaSy-Ro2Ceeti0hHhsUj6Y,468
7
7
  qlv_helper/controller/domestic_activity_order.py,sha256=MlmsDVsMBWq2h4Yjh1rhO372Z3p8tu2-4IZGP-nkfr8,1136
8
8
  qlv_helper/controller/main_page.py,sha256=p_-nXAIptNrx1SUgma7oPB-cqFFHYdCNvXCfsesIQFc,1855
9
- qlv_helper/controller/order_detail.py,sha256=pdxN6ahsoeJq2XqBNRUwLrGG12WSy737fU17fhJMjgg,25621
10
- qlv_helper/controller/order_table.py,sha256=unLL1xMKK1xvm_iewpdMcUPZc6AcBcLq0hAGXAdjdhU,15373
9
+ qlv_helper/controller/order_detail.py,sha256=bDmA_v0CummPlKKfxTry6-SaH6qfZ4LekqnK38Rq2CA,22114
10
+ qlv_helper/controller/order_table.py,sha256=bVVCM2oJF4q43fdJN5aBYbpwzW2rcL0bMoCP90ZIvtY,15144
11
11
  qlv_helper/controller/user_login.py,sha256=oXO1otTGuK3_r07fKdncQE3Is2w-kiI-mWmJyFvsVyE,5915
12
12
  qlv_helper/controller/wechat_login.py,sha256=0u9H6tJiQO28nT5AG2Ot7EWFmpBqFJD_9dd12snsJIQ,2196
13
13
  qlv_helper/http/__init__.py,sha256=yDh1xi_o7ohXqDAzLu62qCWIGk4_aD1dhnUaCon3klM,484
14
14
  qlv_helper/http/main_page.py,sha256=LTpwrG8H_NqwCa3185irgcGd6JQkChAk7HQsDM-TNTI,1519
15
- qlv_helper/http/order_page.py,sha256=Sti3dxZIN1U9z0e7NwyJ6_TrNdI-N3mRFnUN8WpN3FM,21674
15
+ qlv_helper/http/order_page.py,sha256=aaghRwvC2Px5OgQh8Lc4-Q1eQWbXE-lYM2iLdm7qbSw,24987
16
16
  qlv_helper/http/order_table_page.py,sha256=IaXn5wjqPi1aRXHz0kucdHEdZswUWAZfECC13y57y8k,14440
17
17
  qlv_helper/po/__init__.py,sha256=eDr06o0eYapBsYpOhA11bbxzs2F0dsuDjOKmxk_2HVE,480
18
18
  qlv_helper/po/domestic_activity_order_page.py,sha256=El63U0GI2PU9WGIkGxPgCE1Fp1yCsw2v1H6cZhSaG4c,8509
19
19
  qlv_helper/po/login_page.py,sha256=JbapNFwGCei3K3mpfhte1TVNeqzG7yHsCtp5KiKv_6g,4271
20
20
  qlv_helper/po/main_page.py,sha256=0_tqZILZnLS6y3chg9ERTHDtg86pW0aIQ1vNuPFLVG8,1752
21
- qlv_helper/po/order_detail_page.py,sha256=0Xyi9zxnSHv4OfFAlPPz_D0S5S0ZHKLphUUT8R4svzk,14414
21
+ qlv_helper/po/order_detail_page.py,sha256=f3PZF1Sm5Xdc9qRQDDKSq08L6PM-o4Vkchu32sZytfA,15545
22
22
  qlv_helper/po/wechat_auth_page.py,sha256=a4YZlM5JOS0l3CNJm_oJFBhZY7AbNOpdIwilSjAO5bY,3171
23
23
  qlv_helper/utils/__init__.py,sha256=rGzBkUf1tslG4WRPQjVWTVuwWG76pkckuKO_6K4sEus,465
24
24
  qlv_helper/utils/browser_utils.py,sha256=mKoqSEz1vFrVemp9cgI4R5UhA4k7i0Cd9cWsIXJZ6E4,986
@@ -30,7 +30,7 @@ qlv_helper/utils/po_utils.py,sha256=SwQKL58HERGG2Weou_AwY_TQoYSvgi0gvaVCJBput_k,
30
30
  qlv_helper/utils/stealth_browser.py,sha256=srNOYJOboYo30TvW5OP5TaVpg4jgHm9GxqmYnuwcUQU,3140
31
31
  qlv_helper/utils/type_utils.py,sha256=S5FXUje2mbDuq27LU05WymxNu1VGOLBUV3tuqcx51dE,3792
32
32
  qlv_helper/utils/windows_utils.py,sha256=Cvedsk1c2ujgPNVxszz8XWANkvEr8G9kne6povtZRU4,2866
33
- python_qlv_helper-0.7.2.dist-info/METADATA,sha256=LuxKTcs6bOcTbrthW-Q3N35aT9nF7sTKDDZS0uae9bQ,14825
34
- python_qlv_helper-0.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
- python_qlv_helper-0.7.2.dist-info/top_level.txt,sha256=0pYdhD8SfBcC57LzLYGHY7cwwPqdqAkB1twysCJh5OA,11
36
- python_qlv_helper-0.7.2.dist-info/RECORD,,
33
+ python_qlv_helper-0.9.3.dist-info/METADATA,sha256=GVMfOfy40GcPrKIH_pLtFCW3vzPD6b2sZH-yd4XbnW4,14825
34
+ python_qlv_helper-0.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ python_qlv_helper-0.9.3.dist-info/top_level.txt,sha256=0pYdhD8SfBcC57LzLYGHY7cwwPqdqAkB1twysCJh5OA,11
36
+ python_qlv_helper-0.9.3.dist-info/RECORD,,
@@ -13,12 +13,19 @@ import aiohttp
13
13
  import asyncio
14
14
  from logging import Logger
15
15
  from datetime import datetime
16
+ from typing import Dict, Any, List, Optional
16
17
  import qlv_helper.config.url_const as url_const
17
- from typing import Dict, Any, List, cast, Optional
18
18
  from qlv_helper.po.order_detail_page import OrderDetailPage
19
+ from flight_helper.utils.exception_utils import UserNotLoginError
19
20
  from flight_helper.models.dto.procurement import FillProcurementInputDTO
21
+ from flight_helper.utils.exception_utils import RelationOrderConflictError, LockedOrderFailedError
20
22
  from qlv_helper.http.order_page import parser_order_info, get_order_page_html, parser_order_flight_table, \
21
- fill_procurement_info_with_http, fill_itinerary_info_with_http, fill_procurement_dto_with_http
23
+ fill_procurement_dto_with_http as _fill_procurement_dto_with_http, fill_remark_with_http as _fill_remark_with_http, \
24
+ update_policy_with_http as _update_policy_with_http, unlock_order_with_http as _unlock_order_with_http, \
25
+ forced_unlock_order_with_http as _forced_unlock_order_with_http, locked_order_with_http as _locked_order_with_http, \
26
+ fill_itinerary_with_http as _fill_itinerary_with_http, \
27
+ kick_out_activity_order_with_http as _kick_out_activity_order_with_http, \
28
+ kick_out_activity_orders_with_http as _kick_out_activity_orders_with_http
22
29
  from playwright.async_api import Page, Locator, Error as PlaywrightError, TimeoutError as PlaywrightTimeoutError
23
30
 
24
31
 
@@ -57,9 +64,14 @@ async def open_order_detail_page(
57
64
  logger.info(f"即将劲旅订单详情页面,页面URL<{order_detail_url}>")
58
65
 
59
66
  try:
67
+ message: str = await order_detail_po.get_message_content(timeout=1)
68
+ if "关联订单" in message:
69
+ raise RelationOrderConflictError(message)
60
70
  confirm_btn = await order_detail_po.get_message_notice_dialog_confirm_btn(timeout=1)
61
71
  await confirm_btn.click(button="left")
62
72
  logger.info("订单详情页面,消息提醒弹框,【确认】按钮点击完成")
73
+ except (RelationOrderConflictError,):
74
+ raise
63
75
  except (PlaywrightTimeoutError, PlaywrightError, RuntimeError, Exception):
64
76
  pass
65
77
  return order_detail_po
@@ -77,6 +89,58 @@ async def add_custom_remark(*, logger: Logger, page: OrderDetailPage, remark: st
77
89
  logger.info(f"订单详情页面,日志记录栏,【保存备注】按钮点击完成")
78
90
 
79
91
 
92
+ async def add_custom_remark_with_http(
93
+ *, order_id: int, remark: str, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
94
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
95
+ ) -> Dict[str, Any]:
96
+ return await _fill_remark_with_http(
97
+ remark=remark, order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=timeout,
98
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
99
+ )
100
+
101
+
102
+ async def update_order_policy_with_http(
103
+ *, order_id: int, policy_name: str, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
104
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, old_policy_name: Optional[str] = None,
105
+ playwright_state: Dict[str, Any] = None
106
+ ) -> Dict[str, Any]:
107
+ return await _update_policy_with_http(
108
+ policy_name=policy_name, order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry,
109
+ timeout=timeout, enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state,
110
+ old_policy_name=old_policy_name
111
+ )
112
+
113
+
114
+ async def locked_order_with_http(
115
+ *, order_id: int, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
116
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
117
+ ) -> Dict[str, Any]:
118
+ return await _locked_order_with_http(
119
+ order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=timeout,
120
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
121
+ )
122
+
123
+
124
+ async def unlock_order_with_http(
125
+ *, order_id: int, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
126
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
127
+ ) -> Dict[str, Any]:
128
+ return await _unlock_order_with_http(
129
+ order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=timeout,
130
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
131
+ )
132
+
133
+
134
+ async def forced_unlock_order_with_http(
135
+ *, order_id: int, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
136
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
137
+ ) -> Dict[str, Any]:
138
+ return await _forced_unlock_order_with_http(
139
+ order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=timeout,
140
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
141
+ )
142
+
143
+
80
144
  async def update_order_policy(
81
145
  *, page: OrderDetailPage, logger: Logger, policy: str, order_id: int, timeout: float = 5.0
82
146
  ) -> None:
@@ -93,6 +157,13 @@ async def order_unlock(
93
157
  *, logger: Logger, page: OrderDetailPage, remark: str, order_id: int, is_force: bool = False,
94
158
  qlv_user_id: Optional[str] = None, timeout: float = 5.0
95
159
  ) -> None:
160
+ try:
161
+ confirm_btn = await page.get_message_notice_dialog_confirm_btn(timeout=3)
162
+ await confirm_btn.click(button="left")
163
+ logger.info("订单详情页面,消息提醒弹框,【确认】按钮点击完成")
164
+ await asyncio.sleep(2)
165
+ except (Exception,):
166
+ pass
96
167
  # 1. 获取订单操作锁的状态
97
168
  lock_state, lock_btn = await page.get_order_lock_state_btn(timeout=timeout)
98
169
  if "强制解锁" in lock_state:
@@ -101,7 +172,7 @@ async def order_unlock(
101
172
  await lock_btn.click(button="left")
102
173
  logger.info(f"订单详情页面,用户操作【{lock_state}】按钮点击完成")
103
174
  try:
104
- confirm_btn = await page.get_message_notice_dialog_confirm_btn(timeout=1)
175
+ confirm_btn = await page.get_message_notice_dialog_confirm_btn(timeout=5)
105
176
  await confirm_btn.click(button="left")
106
177
  logger.info("订单详情页面,强制解锁弹框确认,【确认】按钮点击完成")
107
178
  await asyncio.sleep(2)
@@ -115,10 +186,27 @@ async def order_unlock(
115
186
  return
116
187
  elif "操作" in lock_state:
117
188
  await lock_btn.click(button="left")
118
- await asyncio.sleep(2)
119
- lock_state_temp = lock_state
189
+ logger.info(f"订单详情页面,日志记录栏,【{lock_state}】按钮点击完成")
190
+ try:
191
+ confirm_btn = await page.get_message_notice_dialog_confirm_btn(timeout=3)
192
+ await confirm_btn.click(button="left")
193
+ logger.info("订单详情页面,消息提醒弹框,【确认】按钮点击完成")
194
+ await asyncio.sleep(2)
195
+ except (Exception,):
196
+ pass
120
197
  lock_state, lock_btn = await page.get_order_lock_state_btn(timeout=timeout)
121
- logger.info(f"订单详情页面,日志记录栏,【{lock_state_temp}】按钮点击完成")
198
+ logger.info(f"订单详情页面,页面刷新后,【{lock_state}】按钮获取完成")
199
+ if "操作" in lock_state:
200
+ await lock_btn.click(button="left")
201
+ logger.info(f"订单详情页面,日志记录栏,【{lock_state}】按钮点击完成")
202
+ try:
203
+ confirm_btn = await page.get_message_notice_dialog_confirm_btn(timeout=3)
204
+ await confirm_btn.click(button="left")
205
+ logger.info("订单详情页面,消息提醒弹框,【确认】按钮点击完成")
206
+ await asyncio.sleep(2)
207
+ except (Exception,):
208
+ pass
209
+ lock_state, lock_btn = await page.get_order_lock_state_btn(timeout=timeout)
122
210
  if remark:
123
211
  # 2. 添加解锁备注
124
212
  await add_custom_remark(logger=logger, page=page, remark=remark, timeout=timeout)
@@ -155,11 +243,19 @@ async def order_locked(
155
243
  # 2. 点击【锁定|操作】按钮
156
244
  await lock_btn.click(button="left")
157
245
  logger.info(f"订单详情页面,日志记录栏,【{lock_state}】按钮点击完成")
246
+ try:
247
+ confirm_btn = await page.get_message_notice_dialog_confirm_btn(timeout=1)
248
+ await confirm_btn.click(button="left")
249
+ logger.info("订单详情页面,强制解锁弹框确认,【确认】按钮点击完成")
250
+ await asyncio.sleep(2)
251
+ except (PlaywrightTimeoutError, PlaywrightError, RuntimeError, Exception):
252
+ pass
158
253
 
159
254
 
160
255
  async def first_open_page_order_locked(
161
256
  *, logger: Logger, page: Page, protocol: str, domain: str, order_id: int, is_force: bool = False,
162
- qlv_user_id: Optional[str] = None, timeout: float = 5.0, **kwargs
257
+ qlv_user_id: Optional[str] = None, timeout: float = 5.0, retry: int = 1, enable_log: bool = True,
258
+ cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None, **kwargs
163
259
  ) -> OrderDetailPage:
164
260
  # 1. 打开页面
165
261
  order_detail_po = await open_order_detail_page(
@@ -167,141 +263,30 @@ async def first_open_page_order_locked(
167
263
  )
168
264
 
169
265
  # 2. 锁定订单
170
- await order_locked(
171
- logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, is_force=is_force,
172
- qlv_user_id=qlv_user_id
173
- )
174
- return order_detail_po
175
-
176
-
177
- async def first_open_page_order_unlock(
178
- *, logger: Logger, page: Page, protocol: str, domain: str, order_id: int, is_force: bool = False,
179
- qlv_user_id: Optional[str] = None, timeout: float = 5.0, remark: Optional[str] = None, **kwargs
180
- ) -> OrderDetailPage:
181
- # 1. 打开页面
182
- order_detail_po = await open_order_detail_page(
183
- page=page, logger=logger, protocol=protocol, domain=domain, order_id=order_id, timeout=timeout
184
- )
185
-
186
- # 2. 解锁订单
187
- await order_unlock(
188
- logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, is_force=is_force,
189
- qlv_user_id=qlv_user_id, remark=remark
190
- )
191
- return order_detail_po
192
-
193
-
194
- async def fill_procurement_info(
195
- *, logger: Logger, page: OrderDetailPage, order_id: int, out_ticket_platform_type: str, purchase_amount: float,
196
- out_ticket_platform: str, out_ticket_account: str, purchase_account_type: str, purchase_account: str,
197
- ceair_user_id: str, ceair_password: str, payment_id: str, platform_order_id: str, timeout: float = 5.0
198
- ) -> None:
199
- # 1. 出票地类型选择【out_ticket_platform_type】
200
- out_ticket_platform_type_dropdown = await page.get_out_ticket_platform_type_dropdown(timeout=timeout)
201
- await out_ticket_platform_type_dropdown.select_option(label=out_ticket_platform_type)
202
- # await out_ticket_platform_type_dropdown.click(button="left")
203
- # out_ticket_platform_type_select_option = await page.get_out_ticket_platform_type_select_option(
204
- # select_option=out_ticket_platform_type, timeout=timeout
205
- # )
206
- # await out_ticket_platform_type_select_option.click(button="left")
207
- logger.info(f"订单详情页面,采购信息栏,出票地类型选择<{out_ticket_platform_type}>已完成")
208
- await asyncio.sleep(1)
209
-
210
- # 2. 出票平台选择【out_ticket_platform】
211
- out_ticket_platform_dropdown = await page.get_out_ticket_platform_dropdown(timeout=timeout)
212
- await out_ticket_platform_dropdown.select_option(label=out_ticket_platform)
213
- # await out_ticket_platform_dropdown.click(button="left")
214
- # out_ticket_platform_select_option = await page.get_out_ticket_platform_select_option(
215
- # select_option=out_ticket_platform, timeout=timeout
266
+ # await order_locked(
267
+ # logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, is_force=is_force,
268
+ # qlv_user_id=qlv_user_id
216
269
  # )
217
- # await out_ticket_platform_select_option.click(button="left")
218
- logger.info(f"订单详情页面,采购信息栏,出票平台选择<{out_ticket_platform}>已完成")
219
- await asyncio.sleep(1)
220
-
221
- # 3. 出票账号选择【out_ticket_account】
222
- out_ticket_account_dropdown = await page.get_out_ticket_account_dropdown(timeout=timeout)
223
- await out_ticket_account_dropdown.select_option(label=out_ticket_account)
224
- # await out_ticket_account_dropdown.click(button="left")
225
- # out_ticket_account_select_option = await page.get_out_ticket_account_select_option(
226
- # select_option=out_ticket_account, timeout=timeout
227
- # )
228
- # await out_ticket_account_select_option.click(button="left")
229
- logger.info(f"订单详情页面,采购信息栏,出票账号选择<{out_ticket_account}>已完成")
230
- await asyncio.sleep(1)
231
-
232
- # 4. 采购账号类型选择【purchase_account_type】
233
- purchase_account_type_dropdown = await page.get_purchase_account_type_dropdown(timeout=timeout)
234
- await purchase_account_type_dropdown.select_option(label=purchase_account_type)
235
- # await purchase_account_type_dropdown.click(button="left")
236
- # purchase_account_type_select_option = await page.get_purchase_account_type_select_option(
237
- # select_option=purchase_account_type, timeout=timeout
238
- # )
239
- # await purchase_account_type_select_option.click(button="left")
240
- logger.info(f"订单详情页面,采购信息栏,采购账号类型选择<{purchase_account_type}>已完成")
241
- await asyncio.sleep(1)
242
-
243
- # 5. 采购账号选择【purchase_account】
244
- purchase_account_dropdown = await page.get_purchase_account_dropdown(timeout=timeout)
245
- await purchase_account_dropdown.select_option(label=purchase_account)
246
- # await purchase_account_dropdown.click(button="left")
247
- # purchase_account_select_option = await page.get_purchase_account_select_option(
248
- # select_option=purchase_account, timeout=timeout
249
- # )
250
- # await purchase_account_select_option.click(button="left")
251
- logger.info(f"订单详情页面,采购信息栏,采购账号选择<{purchase_account}>已完成")
252
- await asyncio.sleep(1)
253
-
254
- # 6. 填写采购金额
255
- purchase_amount_input = await page.get_purchase_amount_input(timeout=timeout)
256
- await purchase_amount_input.fill(value=str(purchase_amount))
257
- logger.info(f"订单详情页面,采购信息栏,采购金额<{purchase_amount}>输入完成")
258
-
259
- # 7. 填写备注
260
- remark_input = await page.get_remark_input(timeout=timeout)
261
- value = f"{ceair_user_id}/{ceair_password}"
262
- await remark_input.fill(value=value)
263
- logger.info(f"订单详情页面,采购信息栏,备注<{value}>输入完成")
264
-
265
- # 8. 填写对账标识
266
- main_check_input = await page.get_main_check_input(timeout=timeout)
267
- await main_check_input.fill(value=payment_id)
268
- logger.info(f"订单详情页面,采购信息栏,对账标识<{payment_id}>输入完成")
269
-
270
- # 9. 填写官网订单号
271
- air_comp_order_id_input = await page.get_air_comp_order_id_input(timeout=timeout)
272
- await air_comp_order_id_input.fill(value=platform_order_id)
273
- logger.info(f"订单详情页面,采购信息栏,官网订单号<{platform_order_id}>输入完成")
274
-
275
- # 10. 点击【保存采购】按钮
276
- procurement_info_save_btn = await page.get_procurement_info_save_btn(timeout=timeout)
277
- await procurement_info_save_btn.click(button="left")
278
- logger.info(f"订单详情页面,采购信息栏,【保存采购】按钮点击完成")
279
-
280
-
281
- async def first_open_page_fill_procurement_info(
282
- *, logger: Logger, page: Page, order_id: int, protocol: str, domain: str, out_ticket_platform_type: str,
283
- purchase_amount: float, out_ticket_platform: str, out_ticket_account: str, purchase_account_type: str,
284
- purchase_account: str, ceair_user_id: str, ceair_password: str, payment_id: str, platform_order_id: str,
285
- is_force: bool = False, qlv_user_id: Optional[str] = None, timeout: float = 5.0, **kwargs: Any
286
- ) -> OrderDetailPage:
287
- # 1. 打开页面
288
- order_detail_po = await open_order_detail_page(
289
- page=page, logger=logger, protocol=protocol, domain=domain, order_id=order_id, timeout=timeout
290
- )
291
-
292
- # 2. 锁定订单
293
- await order_locked(
294
- logger=logger, page=order_detail_po, order_id=order_id, is_force=is_force, timeout=timeout,
295
- qlv_user_id=qlv_user_id
296
- )
297
-
298
- # 3. 回填采购信息
299
- await fill_procurement_info(
300
- out_ticket_platform_type=out_ticket_platform_type, out_ticket_account=out_ticket_account, payment_id=payment_id,
301
- purchase_amount=purchase_amount, out_ticket_platform=out_ticket_platform, purchase_account=purchase_account,
302
- purchase_account_type=purchase_account_type, ceair_user_id=ceair_user_id, ceair_password=ceair_password,
303
- platform_order_id=platform_order_id, timeout=timeout, logger=logger, page=order_detail_po, order_id=order_id
270
+ response = await _locked_order_with_http(
271
+ order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=int(timeout),
272
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
304
273
  )
274
+ message = response.get("message")
275
+ if message == "订单出票":
276
+ logger.info(f"订单<{order_id}>,被用户<{qlv_user_id}>锁单成功")
277
+ else:
278
+ msg: str = "锁单失败"
279
+ if message == "强旅系统":
280
+ if response.get("data").find("用户登陆") != -1:
281
+ raise UserNotLoginError(f"{msg},用户<{qlv_user_id}>未登录劲旅系统")
282
+ else:
283
+ msg = msg + "," + response.get("data") if len(
284
+ response.get("data")
285
+ ) < 100 else response.get("data")[:100]
286
+ raise RuntimeError(msg)
287
+ elif message == "操作提示":
288
+ msg = msg + f",原因:{response.get('data')}"
289
+ raise LockedOrderFailedError(msg)
305
290
  return order_detail_po
306
291
 
307
292
 
@@ -321,26 +306,23 @@ async def fill_itinerary(
321
306
  raise RuntimeError(
322
307
  f"传递回填票号的乘客证件信息<{kwargs_passengers}>与订单实际乘客信息<{current_passengers}>不一致"
323
308
  )
324
- passenger_itinerary_locator = cast(Locator, None)
325
309
  for passenger_locator in passenger_itinerary_locators:
326
310
  current_passenger = passenger_locator.get("username")
327
311
  passenger_itinerary = passengers_itinerary.get(current_passenger)
328
312
  if passenger_itinerary:
329
313
  passenger_itinerary_locator: Locator = passenger_locator.get("locator")
330
314
  await passenger_itinerary_locator.fill(value=passenger_itinerary)
315
+ await passenger_itinerary_locator.press("Enter")
331
316
  logger.info(f"订单<{order_id}>,乘客<{current_passenger}>的票号<{passenger_itinerary}>填写完成")
317
+ await asyncio.sleep(1)
332
318
  else:
333
319
  logger.warning(f"订单<{order_id}>,乘客<{current_passenger}>的票号没有获取到,本次回填跳过,等待下一次")
334
- if passenger_itinerary_locator:
335
- await passenger_itinerary_locator.press("Enter")
336
- logger.info(f"订单<{order_id}>,本次的票号回填完成")
337
- else:
338
- raise RuntimeError(f"回填票号过程异常,回填失败")
339
320
 
340
321
 
341
322
  async def first_open_page_fill_itinerary(
342
- *, page: Page, logger: Logger, protocol: str, domain: str, order_id: int,
343
- passengers_itinerary: Dict[str, Any], timeout: float = 20.0, **kwargs: Any
323
+ *, page: Page, logger: Logger, protocol: str, domain: str, order_id: int, retry: int = 1,
324
+ enable_log: bool = True, passengers_itinerary: Dict[str, Any], timeout: float = 20.0,
325
+ cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None, **kwargs: Any
344
326
  ) -> OrderDetailPage:
345
327
  # 1. 开发页面
346
328
  order_detail_po = await open_order_detail_page(
@@ -353,25 +335,35 @@ async def first_open_page_fill_itinerary(
353
335
  passengers_itinerary=passengers_itinerary
354
336
  )
355
337
  logger.info(f"订单<{order_id}>,票号回填流程结束")
356
- await order_unlock(logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, remark="")
357
- logger.info(f"订单<{order_id}>,订单解锁成功")
358
- return order_detail_po
359
-
360
-
361
- async def first_open_page_update_policy(
362
- *, page: Page, logger: Logger, protocol: str, domain: str, order_id: int, policy: str, timeout: float = 20.0,
363
- **kwargs: Any
364
- ) -> OrderDetailPage:
365
- # 1. 打开页面
366
- order_detail_po = await open_order_detail_page(
367
- page=page, logger=logger, protocol=protocol, domain=domain, order_id=order_id, timeout=timeout
368
- )
369
-
370
- # 2. 更新订单政策代码
371
- await update_order_policy(
372
- logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, policy=policy
373
- )
374
338
 
339
+ # 3. 解锁订单
340
+ # await order_unlock(
341
+ # logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, is_force=True, remark=""
342
+ # )
343
+ msg: str = f"订单<{order_id}>,"
344
+ try:
345
+ response = await forced_unlock_order_with_http(
346
+ retry=retry, timeout=int(timeout), enable_log=enable_log, cookie_jar=cookie_jar,
347
+ playwright_state=playwright_state, order_id=order_id, domain=domain, protocol=protocol
348
+ )
349
+ if response.get("data") == "1":
350
+ logger.info(f"{msg}强制解锁成功")
351
+ except Exception as e:
352
+ logger.error(f"{msg}强制解锁失败,原因:{e}")
353
+ try:
354
+ response = await unlock_order_with_http(
355
+ retry=retry, timeout=int(timeout), enable_log=enable_log, cookie_jar=cookie_jar,
356
+ playwright_state=playwright_state, order_id=order_id, domain=domain, protocol=protocol
357
+ )
358
+ if response.get("data") == "OK":
359
+ logger.info(f"{msg}解锁成功")
360
+ else:
361
+ if response.get('data'):
362
+ logger.error(f"{msg}解锁失败,原因:{response.get('data')}")
363
+ else:
364
+ logger.error(f"{msg}解锁失败,原因:{response}")
365
+ except Exception as e:
366
+ logger.error(f"{msg}解锁失败,原因:{e}")
375
367
  return order_detail_po
376
368
 
377
369
 
@@ -379,7 +371,7 @@ async def first_open_page_my_order_unlock(
379
371
  *, page: Page, logger: Logger, protocol: str, domain: str, order_id: int, qlv_user_id: str,
380
372
  policy: Optional[str] = None, remark: Optional[str] = None, timeout: float = 20.0, is_force: bool = False,
381
373
  **kwargs: Any
382
- ) -> OrderDetailPage:
374
+ ) -> None:
383
375
  # 1. 打开页面
384
376
  order_detail_po = await open_order_detail_page(
385
377
  page=page, logger=logger, protocol=protocol, domain=domain, order_id=order_id, timeout=timeout
@@ -391,56 +383,10 @@ async def first_open_page_my_order_unlock(
391
383
  logger=logger, page=order_detail_po, order_id=order_id, timeout=timeout, policy=policy
392
384
  )
393
385
 
394
- if remark:
395
- # 3. 添加解锁备注
396
- await add_custom_remark(logger=logger, page=order_detail_po, remark=remark, timeout=timeout)
397
-
398
- # 4. 获取订单操作锁的状态
399
- lock_state, lock_btn = await order_detail_po.get_order_lock_state_btn(timeout=timeout)
400
- if "强制解锁" in lock_state:
401
- if is_force is False:
402
- logger.warning(f"订单<{order_id}>,非本账号<{qlv_user_id}>的锁单,暂不处理,即将跳过")
403
- return order_detail_po
404
- await lock_btn.click(button="left")
405
- logger.info(f"订单详情页面,用户操作【{lock_state}】按钮点击完成")
406
- try:
407
- confirm_btn = await order_detail_po.get_message_notice_dialog_confirm_btn(timeout=1)
408
- await confirm_btn.click(button="left")
409
- logger.info("订单详情页面,强制解锁弹框确认,【确认】按钮点击完成")
410
- await asyncio.sleep(2)
411
- except (PlaywrightTimeoutError, PlaywrightError, RuntimeError, Exception):
412
- pass
413
- logger.warning(f"订单<{order_id}>,已被用户<{qlv_user_id or '无记录'}>强制解锁")
414
- # 再次获取订单操作锁的状态
415
- lock_state, lock_btn = await order_detail_po.get_order_lock_state_btn(timeout=timeout)
416
- elif "锁定" in lock_state:
417
- logger.info(f"订单<{order_id}>,处于无锁状态,无需解锁处理")
418
- return order_detail_po
419
- elif "操作" in lock_state:
420
- await lock_btn.click(button="left")
421
- await asyncio.sleep(2)
422
- lock_state_temp = lock_state
423
- lock_state, lock_btn = await order_detail_po.get_order_lock_state_btn(timeout=timeout)
424
- logger.info(f"订单详情页面,日志记录栏,【{lock_state_temp}】按钮点击完成")
425
-
426
- # 5. 点击【解锁返回】按钮
427
- await lock_btn.click(button="left")
428
- await asyncio.sleep(2)
429
- logger.info(f"订单详情页面,日志记录栏,【{lock_state}】按钮点击完成")
430
- return order_detail_po
431
-
432
-
433
- async def fill_procurement_with_http(
434
- *, order_id: int, qlv_domain: str, amount: float, pre_order_id: str, platform_user_id: str, user_password: str,
435
- passengers: List[str], fids: str, pids: List[str], transaction_id: str, qlv_protocol: str = "http",
436
- retry: int = 1, timeout: int = 5, enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None,
437
- playwright_state: Dict[str, Any] = None, data_list: Optional[List[Dict[str, Any]]] = None, **kwargs: Any
438
- ) -> Dict[str, Any]:
439
- return await fill_procurement_info_with_http(
440
- order_id=order_id, qlv_domain=qlv_domain, amount=amount, pre_order_id=pre_order_id,
441
- platform_user_id=platform_user_id, user_password=user_password, passengers=passengers, fids=fids, pids=pids,
442
- transaction_id=transaction_id, qlv_protocol=qlv_protocol, retry=retry, timeout=timeout, enable_log=enable_log,
443
- cookie_jar=cookie_jar, playwright_state=playwright_state, data_list=data_list
386
+ # 3. 获取订单操作锁的状态
387
+ await order_unlock(
388
+ logger=logger, page=order_detail_po, remark=remark, order_id=order_id, is_force=is_force,
389
+ qlv_user_id=qlv_user_id, timeout=timeout
444
390
  )
445
391
 
446
392
 
@@ -449,48 +395,39 @@ async def fill_procurement_with_http_callback(
449
395
  cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None,
450
396
  data_list: Optional[List[Dict[str, Any]]] = None, **kwargs: Any
451
397
  ) -> Dict[str, Any]:
452
- return await fill_procurement_dto_with_http(
398
+ return await _fill_procurement_dto_with_http(
453
399
  fill_procurement_dto=fill_procurement_dto, retry=retry, timeout=timeout, enable_log=enable_log,
454
400
  cookie_jar=cookie_jar, playwright_state=playwright_state, data_list=data_list
455
401
  )
456
402
 
457
403
 
458
404
  async def fill_itinerary_with_http(
459
- *, page: Page, logger: Logger, qlv_protocol: str, qlv_domain: str, order_id: int, retry: int = 1,
460
- passengers: List[Dict[str, Any]], timeout: float = 20.0, cookie_jar: Optional[aiohttp.CookieJar] = None,
461
- playwright_state: Dict[str, Any] = None, enable_log: bool = True, **kwargs: Any
462
- ) -> bool:
463
- # 1. 打开页面
464
- order_detail_po = await open_order_detail_page(
465
- page=page, logger=logger, protocol=qlv_protocol, domain=qlv_domain, order_id=order_id, timeout=timeout
405
+ *, order_id: int, qlv_domain: str, pid: str, tid: str, transaction_id: str, itinerary_id: str, retry: int = 1,
406
+ qlv_protocol: str = "http", timeout: int = 5, enable_log: bool = True, data: Optional[str] = None,
407
+ cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None, **kwargs: Any
408
+ ) -> Dict[str, Any]:
409
+ return await _fill_itinerary_with_http(
410
+ order_id=order_id, qlv_domain=qlv_domain, pid=pid, tid=tid, transaction_id=transaction_id, data=data,
411
+ itinerary_id=itinerary_id, retry=retry, qlv_protocol=qlv_protocol, timeout=timeout, enable_log=enable_log,
412
+ cookie_jar=cookie_jar, playwright_state=playwright_state
466
413
  )
467
414
 
468
- # 2. 获取采购信息的流水id
469
- purchase_transaction_ids = await order_detail_po.get_purchase_info_transaction_id(timeout=timeout)
470
- purchase_transaction_ids = [x for x in purchase_transaction_ids if x != "0"]
471
- if purchase_transaction_ids:
472
- flag = True
473
- purchase_transaction_ids.sort()
474
- purchase_transaction_id = purchase_transaction_ids[0]
475
- for passenger in passengers:
476
- pid = passenger.get("pid")
477
- tid = passenger.get("tid")
478
- p_name = passenger.get("p_name")
479
- itinerary_id = passenger.get("itinerary_id")
480
- try:
481
- response = await fill_itinerary_info_with_http(
482
- order_id=order_id, qlv_domain=qlv_domain, pid=pid, tid=tid, transaction_id=purchase_transaction_id,
483
- itinerary_id=itinerary_id, retry=retry, qlv_protocol=qlv_protocol, timeout=int(timeout),
484
- enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
485
- )
486
- if response == 200 or "OK" in response.get("data"):
487
- logger.info(f"订单<{order_id}>,乘客<{p_name}>票号<{itinerary_id}>回填成功")
488
- else:
489
- logger.warning(
490
- f'订单<{order_id}>,乘客<{p_name}>票号<{itinerary_id}>回填失败:{response.get("data")}')
491
- flag = False
492
- except (Exception,):
493
- flag = False
494
- return flag
495
- else:
496
- raise EnvironmentError("还未填写采购信息,暂时不能回填票号")
415
+
416
+ async def kick_out_activity_order_with_http(
417
+ *, order_id: int, domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
418
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
419
+ ) -> Dict[str, Any]:
420
+ return await _kick_out_activity_order_with_http(
421
+ order_id=order_id, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=timeout,
422
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
423
+ )
424
+
425
+
426
+ async def kick_out_activity_orders_with_http(
427
+ *, order_ids: List[int], domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5,
428
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
429
+ ) -> Dict[str, Any]:
430
+ return await _kick_out_activity_orders_with_http(
431
+ order_ids=order_ids, qlv_domain=domain, qlv_protocol=protocol, retry=retry, timeout=timeout,
432
+ enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
433
+ )
@@ -53,9 +53,10 @@ async def _get_paginated_order_table(
53
53
  response = await fetch_page_fn(
54
54
  domain=domain, protocol=protocol, retry=retry, timeout=timeout,
55
55
  enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state,
56
- order_http_client=order_http_client, is_end=True
56
+ order_http_client=order_http_client, is_end=False
57
57
  )
58
58
  if response.get("code") != 200:
59
+ await order_http_client.close()
59
60
  return response
60
61
 
61
62
  html = response["data"]
@@ -73,6 +74,7 @@ async def _get_paginated_order_table(
73
74
  "pages": 1
74
75
  })
75
76
  response["data"] = pagination_info
77
+ await order_http_client.close()
76
78
  return response
77
79
 
78
80
  # --- 3. 多页:并发抓取第 2~pages 页 ---
@@ -82,7 +84,7 @@ async def _get_paginated_order_table(
82
84
  resp = await fetch_page_fn(
83
85
  domain=domain, protocol=protocol, retry=retry, timeout=timeout,
84
86
  enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state,
85
- order_http_client=client, current_page=page, pages=pages, is_end=(page == pages)
87
+ order_http_client=client, current_page=page, pages=pages, is_end=False
86
88
  )
87
89
  if resp.get("code") == 200:
88
90
  return parse_order_table(html=resp["data"], table_state=table_state)
@@ -90,16 +92,6 @@ async def _get_paginated_order_table(
90
92
  return list() # 抓取失败则返回空,不影响整体
91
93
  return list()
92
94
 
93
- # 🔥 并发:一口气抓全部分页
94
- order_http_client = HttpClientFactory(
95
- protocol=protocol if protocol == "http" else "https",
96
- domain=domain,
97
- timeout=timeout,
98
- retry=retry,
99
- enable_log=enable_log,
100
- cookie_jar=cookie_jar,
101
- playwright_state=playwright_state
102
- )
103
95
  tasks = [fetch_page(client=order_http_client, page=page) for page in range(2, pages + 1)]
104
96
  results = await asyncio.gather(*tasks)
105
97
 
@@ -116,6 +108,7 @@ async def _get_paginated_order_table(
116
108
  "pages": 1
117
109
  })
118
110
  response["data"] = pagination_info
111
+ await order_http_client.close()
119
112
  return response
120
113
 
121
114
 
@@ -12,6 +12,7 @@
12
12
  import re
13
13
  import json
14
14
  import aiohttp
15
+ from random import random
15
16
  from datetime import datetime
16
17
  from urllib.parse import quote
17
18
  from bs4 import BeautifulSoup, Tag
@@ -44,46 +45,6 @@ async def get_order_page_html(
44
45
  )
45
46
 
46
47
 
47
- async def fill_procurement_info_with_http(
48
- *, order_id: int, qlv_domain: str, amount: float, pre_order_id: str, platform_user_id: str, user_password: str,
49
- passengers: List[str], fids: str, pids: List[str], transaction_id: str, qlv_protocol: str = "http",
50
- retry: int = 1, timeout: int = 5, enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None,
51
- playwright_state: Dict[str, Any] = None, data_list: Optional[List[Dict[str, Any]]] = None
52
- ) -> Dict[str, Any]:
53
- client = HttpClientFactory(
54
- protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, enable_log=enable_log, retry=retry,
55
- cookie_jar=cookie_jar or aiohttp.CookieJar(), 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
-
87
48
  async def fill_procurement_dto_with_http(
88
49
  *, fill_procurement_dto: FillProcurementInputDTO, retry: int = 1, timeout: int = 5, enable_log: bool = True,
89
50
  cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None,
@@ -138,9 +99,96 @@ async def fill_procurement_dto_with_http(
138
99
  )
139
100
 
140
101
 
141
- async def fill_itinerary_info_with_http(
102
+ async def fill_remark_with_http(
103
+ *, remark: str, order_id: int, qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
104
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
105
+ ) -> Dict[str, Any]:
106
+ client = HttpClientFactory(
107
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
108
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
109
+ )
110
+
111
+ headers = {
112
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
113
+ }
114
+ data = f"orderid={order_id}&remark={remark}"
115
+ return await client.request(
116
+ method="POST", url="/JPOrderCommon/SaveFreeRemark", headers=headers, is_end=True, data=data.encode("utf-8")
117
+ )
118
+
119
+
120
+ async def update_policy_with_http(
121
+ *, policy_name: str, order_id: int, qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
122
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, old_policy_name: Optional[str] = None,
123
+ playwright_state: Dict[str, Any] = None,
124
+ ) -> Dict[str, Any]:
125
+ client = HttpClientFactory(
126
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
127
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
128
+ )
129
+
130
+ headers = {
131
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
132
+ }
133
+ data = f"OrderID={order_id}&PolicyName={policy_name}&OldPolicyName="
134
+ if old_policy_name is not None:
135
+ data = f"{data}{old_policy_name}"
136
+ return await client.request(
137
+ method="POST", url="/OrderProcessing/EditPolicyName", headers=headers, is_end=True, data=data.encode("utf-8")
138
+ )
139
+
140
+
141
+ async def locked_order_with_http(
142
+ *, order_id: int, qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
143
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
144
+ ) -> Dict[str, Any]:
145
+ client = HttpClientFactory(
146
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
147
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
148
+ )
149
+
150
+ result = await client.request(method="GET", url=f"/OrderProcessing/Ticket/{order_id}", is_end=True)
151
+ if "操作提示" in result.get("message", ""):
152
+ soup = BeautifulSoup(result.get("data"), 'html.parser')
153
+ h3_tag = soup.find('h3')
154
+ if h3_tag:
155
+ text = h3_tag.get_text(strip=True)
156
+ if text:
157
+ result["data"] = text
158
+ return result
159
+
160
+
161
+ async def unlock_order_with_http(
162
+ *, order_id: int, qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
163
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
164
+ ) -> Dict[str, Any]:
165
+ client = HttpClientFactory(
166
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
167
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
168
+ )
169
+
170
+ return await client.request(
171
+ method="GET", url=f"/OrderProcessing/UnLock/{order_id}?s={random()}", is_end=True
172
+ )
173
+
174
+
175
+ async def forced_unlock_order_with_http(
176
+ *, order_id: int, qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
177
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
178
+ ) -> Dict[str, Any]:
179
+ client = HttpClientFactory(
180
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
181
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
182
+ )
183
+
184
+ return await client.request(
185
+ method="GET", url=f"/OrderProcessing/UnLockForce/{order_id}?s={random()}", is_end=True
186
+ )
187
+
188
+
189
+ async def fill_itinerary_with_http(
142
190
  *, order_id: int, qlv_domain: str, pid: str, tid: str, transaction_id: str, itinerary_id: str, retry: int = 1,
143
- qlv_protocol: str = "http", timeout: int = 5, enable_log: bool = True,
191
+ qlv_protocol: str = "http", timeout: int = 5, enable_log: bool = True, data: Optional[str] = None,
144
192
  cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
145
193
  ) -> Dict[str, Any]:
146
194
  client = HttpClientFactory(
@@ -152,13 +200,49 @@ async def fill_itinerary_info_with_http(
152
200
  "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
153
201
  "Referer": f"{qlv_protocol}://{qlv_domain}/OrderProcessing/NewTicket_show/{order_id}?&r={datetime.now().strftime("%Y%m%d%H%M%S")}",
154
202
  }
155
- data = f"OrderID={order_id}&OrderPID={pid}&OrderTID={tid}&TicketNo={itinerary_id}&ZJTransactionID={transaction_id}"
203
+ if not data:
204
+ data = f"OrderID={order_id}&OrderPID={pid}&OrderTID={tid}&TicketNo={itinerary_id}&ZJTransactionID={transaction_id}"
156
205
  return await client.request(
157
206
  method="POST", url="/OrderProcessing/TicketNoSave",
158
207
  headers=headers, is_end=True, data=data.encode("utf-8")
159
208
  )
160
209
 
161
210
 
211
+ async def kick_out_activity_order_with_http(
212
+ *, order_id: int, qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
213
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
214
+ ) -> Dict[str, Any]:
215
+ client = HttpClientFactory(
216
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
217
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
218
+ )
219
+ headers = {
220
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
221
+ }
222
+ data = f"ID={order_id}"
223
+ return await client.request(
224
+ method="POST", url=f"/OrderList/KickOutActivityOrders", is_end=True, data=data.encode("utf-8"), headers=headers
225
+ )
226
+
227
+
228
+ async def kick_out_activity_orders_with_http(
229
+ *, order_ids: List[int], qlv_protocol: str, qlv_domain: str, retry: int = 1, timeout: int = 5,
230
+ enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
231
+ ) -> Dict[str, Any]:
232
+ client = HttpClientFactory(
233
+ protocol=qlv_protocol, domain=qlv_domain, timeout=timeout, retry=retry,
234
+ enable_log=enable_log, cookie_jar=cookie_jar or aiohttp.CookieJar(), playwright_state=playwright_state
235
+ )
236
+ headers = {
237
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
238
+ }
239
+ order_ids_str = ",".join(str(id) for id in order_ids) + ","
240
+ data = f"OrderIDS={order_ids_str}"
241
+ return await client.request(
242
+ method="POST", url=f"/OrderList/YJTCActivityOrders", is_end=True, data=data.encode("utf-8"), headers=headers
243
+ )
244
+
245
+
162
246
  def order_info_static_headers() -> OrderedDict[str, str]:
163
247
  return OrderedDict([
164
248
  ("receipted_ota", "OTA实收"), # 0
@@ -263,7 +263,7 @@ class OrderDetailPage(BasePo):
263
263
  :return: (是否存在, 错误信息|元素对象)
264
264
  """
265
265
  selector: str = '//a[@id="alertMsg_btnConfirm"]/cite'
266
- return await self.get_locator(selector=selector, timeout=timeout)
266
+ return await self.get_locator(selector=selector, timeout=timeout, strict=False)
267
267
 
268
268
  async def get_policy_input(self, timeout: float = 5.0) -> Locator:
269
269
  """
@@ -290,3 +290,31 @@ class OrderDetailPage(BasePo):
290
290
  if transaction_id:
291
291
  transaction_ids.append(transaction_id.strip())
292
292
  return transaction_ids
293
+
294
+ async def get_relation_order_detail_link(self, timeout: float = 5.0) -> Locator:
295
+ """
296
+ 获取关联订单的详情链接
297
+ :param timeout: 超时时间
298
+ :return:
299
+ """
300
+ selector: str = 'xpath=//span[contains(text(), "关联订单")]/../a'
301
+ return await self.get_locator(selector=selector, timeout=timeout)
302
+
303
+ async def get_message_alert(self, timeout: float = 5.0) -> Locator:
304
+ """
305
+ 获取详情页面消息弹框
306
+ :param timeout: 超时时长
307
+ :return:
308
+ """
309
+ selector: str = 'xpath=//a[@id="alertMsg_btnConfirm"]/cite/../..'
310
+ return await self.get_locator(selector=selector, timeout=timeout)
311
+
312
+ async def get_message_content(self, timeout: float = 5.0) -> str:
313
+ """
314
+ 获取详情页面消息弹框
315
+ :param timeout: 超时时长
316
+ :return:
317
+ """
318
+ selector: str = 'xpath=//a[@id="alertMsg_btnConfirm"]/cite/../../p'
319
+ locator = await self.get_locator(selector=selector, timeout=timeout)
320
+ return (await locator.inner_text()).strip()