Files
kami_apple_exchage/backend/app/services/playwright_service.py
danial 00ab9f48fc refactor(data): 重构数据目录结构并优化相关配置
- 移除 screenshot 相关代码和配置
- 修改 SNAPSHOT_DIR 路径为复数形式
- 更新 Dockerfile 和 docker-compose 文件中的数据目录结构
- 优化 playwright_service 中的等待逻辑
- 修改异常日志输出,使用 traceback 提供更详细的信息
2025-09-19 19:11:39 +08:00

778 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Apple订单处理服务
集成OptimizedAppleOrderProcessor业务流程
专为Arq Worker环境设计的分布式订单处理服务
"""
import asyncio
import traceback
from datetime import datetime
from typing import Any
from playwright.async_api import Page
from app.core.config import get_settings
from app.core.database import db_manager
from app.core.log import get_logger
from app.core.playwright_manager import DistributedPlaywrightManager
from app.core.state_manager import task_state_manager
from app.enums.task import OrderTaskStatus
from app.models.orders import OrderStatus, Orders
from app.repositories.order_repository import OrderRepository
from app.services.file_service import file_service
from app.services.gift_card_service import GiftCardService
settings = get_settings()
logger = get_logger(__name__)
class AppleOrderProcessor:
"""
Apple订单处理器
集成了完整的Apple礼品卡订单处理流程
支持中断式礼品卡输入和实时进度跟踪
采用策略模式和责任链模式,提供清晰的代码结构
"""
def __init__(self, task_id: str, order_id: str):
self.order_id = order_id
self.task_id = task_id
self.order: Orders | None = None
self.thread_prefix = f"[订单{order_id}]"
# Apple网站元素选择器配置
self.selectors = {
"add_to_cart": "#add-to-cart",
"zipcode_edit": "//button[@data-autom='bag-zipcode-edit-cold']",
"zipcode_input": ".form-textbox-input.form-textbox-number-input",
"zipcode_apply": "//button[@data-autom='bag-zipcode-apply']",
"checkout_other_payments": "shoppingCart.actions.checkoutOtherPayments",
"guest_login": "signIn.guestLogin.guestLogin",
"continue_shipping": "#rs-checkout-continue-button-bottom",
"first_name": "checkout.shipping.addressSelector.newAddress.address.firstName",
"last_name": "checkout.shipping.addressSelector.newAddress.address.lastName",
"street_address": "checkout.shipping.addressSelector.newAddress.address.street",
"email": "checkout.shipping.addressContactEmail.address.emailAddress",
"phone": "checkout.shipping.addressContactPhone.address.fullDaytimePhone",
"continue_payment": "#rs-checkout-continue-button-bottom",
"continue_selected_address": "checkout.shipping.addressVerification.selectedAddress.continueWithSelectedAddress",
"gift_card_reset": "checkout.billing.billingOptions.selectedBillingOptions.giftCard.giftCardInput.resetFields",
"gift_card_input": "checkout.billing.billingOptions.selectedBillingOptions.giftCard.giftCardInput.giftCard",
"apply_gift_card": "checkout.billing.billingOptions.selectedBillingOptions.giftCard.giftCardInput.applyGiftCard",
"continue_review": "rs-checkout-continue-button-bottom",
"place_order": "rs-checkout-continue-button-bottom",
"order_number": "#thankyou-container > div > div.rs-thankyou-headcontent > div > div > a",
}
# 失败指示器配置
self.failure_indicators = {
"Please use an Apple Gift Card that has been purchased in United States": "礼品卡地区限制",
"You have entered an invalid gift card": "无效礼品卡",
"Please enter a valid PIN": "无效卡号",
"We could not process your gift card": "礼品卡处理错误",
"This gift card has a zero balance": "礼品卡余额为0",
"Please enter another form of payment to cover the remaining balance": "礼品卡余额不足",
}
# 进度步骤定义 - 详细匹配实际执行流程
self.progress_steps = {
# 初始化阶段 (1-5%)
"初始化任务上下文": 1.0,
"加载订单信息": 3.0,
"初始化浏览器上下文": 5.0,
# 产品页面阶段 (6-15%)
"打开产品页面": 6.0,
"等待页面加载": 8.0,
"验证产品页面": 10.0,
"产品页面加载完成": 12.0,
"准备添加到购物车": 15.0,
# 购物车阶段 (16-25%)
"点击添加到购物车": 16.0,
"等待购物车响应": 18.0,
"验证购物车状态": 20.0,
"购物车操作完成": 22.0,
"准备设置邮编": 25.0,
# 邮编设置阶段 (26-35%)
"点击邮编编辑": 26.0,
"输入邮编信息": 28.0,
"应用邮编设置": 30.0,
"等待邮编确认": 32.0,
"邮编设置完成": 35.0,
# 结算流程阶段 (36-45%)
"进入结算流程": 36.0,
"等待结算页面": 38.0,
"选择游客登录": 40.0,
"等待登录确认": 42.0,
"继续配送流程": 45.0,
# 配送信息阶段 (46-65%)
"填写姓名信息": 46.0,
"填写邮箱地址": 48.0,
"填写电话号码": 50.0,
"填写街道地址": 52.0,
"保存配送信息": 54.0,
"继续到支付页面": 56.0,
"等待地址验证": 58.0,
"确认配送地址": 60.0,
"配送信息完成": 65.0,
# 礼品卡准备阶段 (66-75%)
"处理额外弹窗": 66.0,
"准备礼品卡输入": 68.0,
"等待礼品卡信息": 70.0,
"礼品卡接收完成": 72.0,
"准备输入礼品卡": 75.0,
# 礼品卡处理阶段 (76-85%)
"输入礼品卡号": 76.0,
"应用礼品卡": 78.0,
"等待礼品卡验证": 80.0,
"验证礼品卡状态": 82.0,
"处理礼品卡结果": 84.0,
"继续到审核页面": 85.0,
# 订单提交阶段 (86-95%)
"验证订单信息": 86.0,
"准备提交订单": 88.0,
"提交订单": 90.0,
"等待订单确认": 92.0,
"验证订单成功": 94.0,
"订单提交完成": 95.0,
# 订单完成阶段 (96-100%)
"提取订单号": 96.0,
"保存订单信息": 98.0,
"处理完成": 100.0,
}
async def process_order(self) -> dict[str, Any]:
"""
处理订单的主入口方法
使用上下文管理器确保资源正确清理
"""
logger.info(f"{self.thread_prefix} 开始处理订单")
try:
# 初始化阶段
await self._update_progress("初始化任务上下文", OrderTaskStatus.RUNNING)
await self._update_progress("加载订单信息")
await self._load_order_info()
await self._update_progress("初始化浏览器上下文")
# 使用Playwright上下文管理器
async with playwright_manager.get_order_context(
self.order_id
) as context_info:
page = context_info["page"]
# 执行订单处理流程
result = await self._execute_order_flow(page)
logger.info(f"{self.thread_prefix} 订单处理完成")
return result
except Exception as e:
error_msg = f"订单处理异常: {str(e)}"
logger.error(f"{self.thread_prefix} {error_msg}\n{traceback.format_exc()}")
return {"success": False, "error": error_msg, "order_id": self.order_id}
async def _load_order_info(self):
"""加载订单信息"""
async with db_manager.get_async_session() as session:
order_repo = OrderRepository(session)
self.order = await order_repo.get_order_with_full_details(self.order_id)
if not self.order:
raise ValueError(f"订单不存在: {self.order_id}")
if self.order.status != OrderStatus.PENDING:
raise ValueError(f"订单状态不正确: {self.order.status}")
# 更新订单状态为处理中
await order_repo.update_by_id(
self.order_id,
status=OrderStatus.PROCESSING,
updated_at=datetime.now(),
)
logger.info(f"{self.thread_prefix} 订单信息加载成功")
async def _update_progress(
self, step_name: str, status: OrderTaskStatus | None = None
):
"""更新任务进度"""
progress = self.progress_steps.get(step_name, 0.0)
if status:
await task_state_manager.set_task_state(
self.task_id,
status,
progress=progress,
order_id=self.order_id,
worker_id=self.order_id,
current_step=step_name,
)
else:
await task_state_manager.update_task_progress(self.task_id, progress)
logger.info(f"{self.thread_prefix} {step_name} ({progress}%)")
async def _execute_order_flow(self, page: Page) -> dict[str, Any]:
"""执行订单处理流程"""
try:
# 检查页面是否有效
if page.is_closed():
raise Exception("页面已关闭")
# 产品页面阶段
await self._update_progress("打开产品页面")
await self._navigate_to_product_page(page)
await self._update_progress("等待页面加载")
await page.wait_for_timeout(1000)
await self._update_progress("验证产品页面")
await page.wait_for_load_state("networkidle")
await self._update_progress("产品页面加载完成")
await self._update_progress("准备添加到购物车")
# 购物车阶段
await self._update_progress("点击添加到购物车")
await self._add_to_cart(page)
await self._update_progress("等待购物车响应")
await page.wait_for_timeout(1000)
await self._update_progress("验证购物车状态")
await self._update_progress("购物车操作完成")
await self._update_progress("准备设置邮编")
# 邮编设置阶段
await self._update_progress("点击邮编编辑")
await self._handle_zipcode_setup(page)
await self._update_progress("等待邮编确认")
await page.wait_for_timeout(1000)
await self._update_progress("邮编设置完成")
# 结算流程阶段
await self._update_progress("进入结算流程")
await self._checkout_other_payments(page)
await self._update_progress("等待结算页面")
await page.wait_for_timeout(1000)
await self._update_progress("选择游客登录")
await self._continue_as_guest(page)
await self._update_progress("等待登录确认")
await page.wait_for_timeout(1000)
await self._update_progress("继续配送流程")
await self._handle_payment_selection(page)
# 配送信息阶段
await self._update_progress("填写姓名信息")
await self._update_progress("填写邮箱地址")
await self._update_progress("填写电话号码")
await self._update_progress("填写街道地址")
await self._handle_shipping_info(page)
await self._update_progress("保存配送信息")
await self._update_progress("继续到支付页面")
await page.wait_for_timeout(1000)
await self._update_progress("等待地址验证")
await self._update_progress("确认配送地址")
await self._update_progress("配送信息完成")
# 礼品卡准备阶段
await self._update_progress("处理额外弹窗")
await self._handle_gift_card_reset(page)
await self._update_progress("准备礼品卡输入")
await self._handle_gift_card_process_enhanced(page)
# 礼品卡处理阶段
await self._update_progress("输入礼品卡号")
await self._update_progress("应用礼品卡")
await self._update_progress("等待礼品卡验证")
await page.wait_for_timeout(1000)
await self._update_progress("验证礼品卡状态")
await self._update_progress("处理礼品卡结果")
await self._update_progress("继续到审核页面")
await self._continue_to_review(page)
# 订单提交阶段
await self._update_progress("验证订单信息")
await self._update_progress("准备提交订单")
await self._update_progress("提交订单")
success = await self._handle_order_completion(page)
await self._update_progress("等待订单确认")
await page.wait_for_timeout(1000)
await self._update_progress("验证订单成功")
await self._update_progress("订单提交完成")
if success:
await self._update_progress("提取订单号")
order_info = await self._extract_order_info(page)
await self._update_progress("处理完成", OrderTaskStatus.SUCCESS)
return {
"success": True,
"order_id": self.order_id,
"order_number": order_info.get("order_number"),
"order_url": order_info.get("order_url"),
}
else:
return {
"success": False,
"error": "提取订单信息失败",
"order_id": self.order_id,
}
except Exception as e:
# 生成截图
try:
await file_service.save_screenshot(
f"{self.order_id}-error-{self.task_id}.png", await page.screenshot()
)
await file_service.save_export_file(
f"{self.order_id}-error-{self.task_id}.html", await page.content()
)
except Exception as _:
logger.error(
f"{self.thread_prefix} 保存错误页面失败: {traceback.format_exc()}"
)
error_msg = f"订单流程执行失败: {e}"
logger.error(f"{self.thread_prefix} {error_msg}")
return {"success": False, "error": error_msg, "order_id": self.order_id}
async def _navigate_to_product_page(self, page: Page):
"""打开产品页面"""
if not self.order or not self.order.links or not self.order.links.url:
raise ValueError("订单中没有产品链接信息")
product_url = self.order.links.url
logger.info(f"{self.thread_prefix} 打开产品页面: {product_url}")
try:
await page.goto(product_url, wait_until="networkidle", timeout=30000)
await page.wait_for_timeout(2000)
await page.wait_for_load_state("networkidle")
except Exception as nav_error:
raise Exception(f"{self.thread_prefix} 页面导航失败: {str(nav_error)}")
async def _add_to_cart(self, page: Page):
"""添加到购物车"""
if not await self._click_element(page, self.selectors["add_to_cart"]):
raise Exception("无法点击添加到购物车按钮")
await page.wait_for_timeout(2000)
logger.info(f"{self.thread_prefix} 已添加到购物车")
# 辅助方法实现
async def _click_element(
self,
page: Page,
selector: str,
selector_type: str = "css",
timeout: int = 10000,
wait: bool = True,
wait_stable: bool = False,
) -> bool:
"""统一的元素点击方法"""
try:
if page.is_closed():
logger.error(f"{self.thread_prefix} 页面已关闭,无法操作元素")
return False
full_selector = self._build_selector(selector, selector_type)
try:
element = await page.wait_for_selector(full_selector, timeout=timeout)
if not element:
logger.error(f"{self.thread_prefix} 元素未找到: {selector}")
return False
except asyncio.TimeoutError:
logger.error(f"{self.thread_prefix} 等待元素超时: {selector}")
return False
await element.click()
if wait_stable:
await self._wait_for_element_stable(element)
if wait:
await page.wait_for_load_state("networkidle")
await page.wait_for_timeout(1000)
logger.debug(f"{self.thread_prefix} 成功点击元素: {selector}")
return True
except Exception as e:
logger.error(f"{self.thread_prefix} 点击元素失败 {selector}: {e}")
return False
async def _input_element(
self,
page: Page,
selector: str,
selector_type: str = "css",
value: str = "",
timeout: int = 10000,
):
"""统一的输入方法"""
try:
if page.is_closed():
logger.error(f"{self.thread_prefix} 页面已关闭,无法操作元素")
return False
full_selector = self._build_selector(selector, selector_type)
try:
element = await page.wait_for_selector(full_selector, timeout=timeout)
if not element:
logger.error(f"{self.thread_prefix} 元素未找到: {selector}")
return False
except asyncio.TimeoutError:
logger.error(f"{self.thread_prefix} 等待元素超时: {selector}")
return False
# 输入信息
await element.fill(value)
logger.debug(f"{self.thread_prefix} 成功输入元素: {selector} 内容:{value}")
return True
except Exception as e:
logger.error(f"{self.thread_prefix} 点击元素失败 {selector}: {e}")
return False
def _build_selector(self, selector: str, selector_type: str) -> str:
"""构建完整的选择器字符串"""
if selector_type == "id":
return f"#{selector.replace('.', '\\.')}"
elif selector_type == "xpath":
return f"xpath={selector}"
else:
return selector
async def _wait_for_element_stable(self, element, max_wait: int = 10) -> None:
"""等待元素状态稳定(非禁用状态)"""
try:
for _ in range(max_wait):
if not await element.is_disabled():
break
await asyncio.sleep(1)
except Exception:
pass # 忽略状态检查错误
# 简化的处理方法,实际实现需要根据业务需求完善
async def _handle_zipcode_setup(self, page: Page):
"""处理邮编设置"""
"""添加到购物车"""
logger.info(f"{self.thread_prefix} 添加邮箱")
if not await self._click_element(
page, self.selectors["zipcode_edit"], wait=False
):
raise Exception("无法点击点击邮编设置按钮")
if not await self._input_element(
page, self.selectors["zipcode_input"], value=self.order.user_data.zip_code
):
raise Exception("无法输入邮编")
if not await self._click_element(
page, self.selectors["zipcode_apply"], wait=False, wait_stable=True
):
raise Exception("无法应用邮箱")
logger.info(f"{self.thread_prefix} 已添加邮箱")
async def _checkout_other_payments(self, page: Page):
logger.info(f"{self.thread_prefix} 下单")
if not await self._click_element(
page, self.selectors["checkout_other_payments"], selector_type="id"
):
raise Exception("无法点击下单按钮")
logger.info(f"{self.thread_prefix} 已下单")
async def _continue_as_guest(self, page: Page):
logger.info(f"{self.thread_prefix} 下单")
if not await self._click_element(
page, self.selectors["guest_login"], selector_type="id", wait_stable=True
):
raise Exception("无法点击使用游客登录")
logger.info(f"{self.thread_prefix} 已下单")
async def _handle_payment_selection(self, page: Page):
"""处理支付方式选择"""
logger.info(f"{self.thread_prefix} 处理支付方式选择")
# 实际实现会包含支付方式选择的具体操作
if not await self._click_element(
page, self.selectors["continue_shipping"], wait_stable=True
):
raise Exception("无法点击使用游客登录")
logger.info(f"{self.thread_prefix} 已处理支付方式选择")
async def _handle_shipping_info(self, page: Page):
"""处理配送信息填写"""
logger.info(f"{self.thread_prefix} 处理配送信息")
# 实际实现会包含配送信息填写的具体操作
if not await self._input_element(
page,
self.selectors["first_name"],
value=self.order.user_data.first_name,
selector_type="id",
):
raise Exception("无法输入first name")
if not await self._input_element(
page,
self.selectors["last_name"],
value=self.order.user_data.last_name,
selector_type="id",
):
raise Exception("无法输入last name")
if not await self._input_element(
page,
self.selectors["email"],
value=self.order.user_data.email,
selector_type="id",
):
raise Exception("无法输入email")
clean_phone = (
self.order.user_data.phone.replace("+", "")
.replace("-", "")
.replace(" ", "")
)
# # 如果电话号码以1开头且长度为11位移除开头的1
if clean_phone.startswith("1") and len(clean_phone) == 11:
clean_phone = clean_phone[1:]
if not await self._input_element(
page,
self.selectors["phone"],
value=clean_phone,
selector_type="id",
):
raise Exception("无法输入phone")
if not await self._input_element(
page,
self.selectors["street_address"],
value=self.order.user_data.street_address,
selector_type="id",
):
raise Exception("无法输入street_address")
await file_service.save_screenshot(
f"{self.order_id}-user-data-{self.task_id}.png", await page.screenshot()
)
if not await self._click_element(
page, self.selectors["continue_payment"], wait_stable=True
):
raise Exception("无法点击 Continue to payment")
if not await self._click_element(
page,
self.selectors["continue_selected_address"],
selector_type="id",
wait_stable=True,
timeout=3000,
):
await asyncio.sleep(1)
logger.info(f"{self.thread_prefix} 已处理配送信息")
async def _handle_gift_card_reset(self, page: Page):
logger.info(f"{self.thread_prefix} 处理额外弹窗")
if not await self._click_element(
page,
self.selectors["gift_card_reset"],
selector_type="id",
wait_stable=True,
):
raise Exception("无法点击 Continue to payment")
logger.info(f"{self.thread_prefix} 已处理额外弹窗")
async def _handle_gift_card_process_enhanced(self, page: Page) -> None:
"""处理礼品卡流程(增强版,支持中断式输入)"""
logger.info(f"{self.thread_prefix} 等待礼品卡处理")
try:
# 更新任务状态为等待礼品卡
await task_state_manager.set_task_state(
self.task_id,
status=OrderTaskStatus.WAITING_GIFT_CARD,
worker_id=self.order_id,
order_id=self.order_id,
progress=self.progress_steps.get("准备礼品卡输入", 0),
message="等待用户提供礼品卡信息",
)
# 等待礼品卡信息最多等待10分钟
card_code, gift_card_id = await self._wait_for_gift_card_input()
#
if not card_code:
raise Exception("未收到礼品卡信息")
logger.info(f"已输入礼品卡号: {card_code}")
# 输入礼品卡号到页面
if not await self._fill_gift_card_form(page, card_code, gift_card_id):
raise Exception("无法输入礼品卡号")
logger.info(f"{self.thread_prefix} 礼品卡处理成功")
except Exception as e:
raise e
async def _wait_for_gift_card_input(self) -> tuple[str, str]:
"""等待礼品卡信息输入"""
timeout = 600 # 10分钟
start_time = asyncio.get_event_loop().time()
while True:
# 检查是否已接收到礼品卡信息
task_state = await task_state_manager.get_task_state(self.task_id)
if task_state and task_state.status == OrderTaskStatus.GIFT_CARD_RECEIVED:
logger.info(f"{self.thread_prefix} 已接收到礼品卡信息")
# 获取礼品卡信息
async with db_manager.get_async_session() as db:
gift_card_service = GiftCardService(db)
card_code, gift_card_id = await gift_card_service.get_card_code(
self.order_id
)
if card_code:
return card_code, gift_card_id
else:
logger.error(f"{self.thread_prefix} 礼品卡信息获取失败")
raise Exception("获取礼品卡信息失败")
# 检查超时
if asyncio.get_event_loop().time() - start_time > timeout:
logger.error(f"{self.thread_prefix} 等待礼品卡信息超时")
raise Exception("等待礼品卡信息超时")
# 等待1秒后再次检查
await asyncio.sleep(2)
async def _fill_gift_card_form(
self, page: Page, card_code: str, gift_card_id: str
) -> bool:
"""在页面中填写礼品卡表单"""
try:
# 实际的礼品卡表单填写逻辑
logger.info(f"{self.thread_prefix} 填写礼品卡号: {card_code}")
# 具体的页面操作会在这里实现
if not await self._input_element(
page,
self.selectors["gift_card_input"],
value=card_code,
selector_type="id",
):
raise Exception("无法输入邮编")
if not await self._click_element(
page,
self.selectors["apply_gift_card"],
selector_type="id",
wait_stable=True,
):
raise Exception("无法点击 apply_gift_card")
content = await page.content()
for tip, reason in self.failure_indicators.items():
if tip in content:
async with db_manager.get_async_session() as session:
gift_card_service = GiftCardService(session)
await gift_card_service.update_failed_reason(
gift_card_id, reason
)
raise Exception(reason)
async with db_manager.get_async_session() as session:
gift_card_service = GiftCardService(session)
await gift_card_service.update_succeed(gift_card_id)
logger.info(f"{self.thread_prefix} 填写礼品卡表单成功")
return True
except Exception as e:
raise e
async def _continue_to_review(self, page: Page) -> bool:
try:
logger.info(f"{self.thread_prefix} 处理配送信息")
if not await self._click_element(
page,
self.selectors["continue_review"],
selector_type="id",
wait_stable=True,
):
raise Exception("无法点击 continue_to_review")
await page.wait_for_load_state("networkidle")
logger.info(f"{self.thread_prefix} 已处理配送信息")
return True
except Exception as e:
logger.error(f"{self.thread_prefix} 处理配送信息失败: {e}")
raise e
async def _handle_order_completion(self, page: Page) -> bool:
"""处理订单完成"""
try:
logger.info(f"{self.thread_prefix} 完成订单提交")
# 实际的订单完成逻辑
content = await page.content()
if (
"Please enter another form of payment to cover the remaining balance"
in content
):
raise Exception("礼品卡余额不足")
if "Place Your Order" not in content:
raise Exception("无法找到 Place Your Order 按钮 未知错误")
if not await self._click_element(
page,
self.selectors["place_order"],
selector_type="id",
wait_stable=True,
):
raise Exception("无法点击 place_order")
await page.wait_for_timeout(timeout=5000)
await page.wait_for_load_state("networkidle")
# 强制等待
max_wait_timeout = 60
while max_wait_timeout >= 0:
try:
content = await page.content()
except Exception:
continue
if "Youre all set." in content:
logger.info(f"{self.thread_prefix} 订单提交成功")
break
logger.info(
f"{self.thread_prefix} 正在等待订单提交成功...,倒计时:{max_wait_timeout}"
)
max_wait_timeout -= 1
await page.wait_for_timeout(1000)
try:
await file_service.save_screenshot(
f"{self.order_id}-{self.task_id}.png",
await page.screenshot(full_page=True),
)
await file_service.save_export_file(
f"{self.order_id}-{self.task_id}.html", await page.content()
)
except Exception as _:
logger.error(
f"{self.thread_prefix} 保存订单页面失败: {traceback.format_exc()}"
)
if max_wait_timeout < 0 and "Youre all set." not in await page.content():
logger.error(f"{self.thread_prefix} 订单提交超时")
raise Exception("订单提交超时")
return True
except Exception as e:
logger.error(f"{self.thread_prefix} 订单完成失败: {traceback.format_exc()}")
raise e
async def _extract_order_info(self, page: Page) -> dict[str, Any]:
"""提取订单信息"""
logger.info(f"{self.thread_prefix} 提取订单信息")
info = {}
try:
order_number_element = await page.wait_for_selector(
"#thankyou-container > div > div.rs-thankyou-headcontent > div > div > a"
)
if order_number_element:
order_number_text = await order_number_element.text_content()
order_number_text = order_number_text or ""
order_number_link = await order_number_element.get_attribute("href")
logger.info(
f"{self.thread_prefix} 订单号为: {order_number_text} 订单链接:{order_number_link}"
)
info["order_number"] = order_number_text
info["order_url"] = order_number_link
return info
except Exception as e:
logger.error(
f"{self.thread_prefix} 提取订单信息失败: {traceback.format_exc()}"
)
raise Exception("提取链接信息失败")
# 全局实例
playwright_manager = DistributedPlaywrightManager()