package services import ( "fmt" "log" "time" "github.com/go-rod/rod" ) // Cart 购物车操作服务 type Cart struct { page *rod.Page } // NewCart 创建购物车服务 func NewCart(page *rod.Page) *Cart { return &Cart{page: page} } // Open 打开购物车页面并等待完全加载 func (c *Cart) Open() error { log.Println("[购物车] 正在打开购物车页面...") c.page.MustNavigate("https://cart.taobao.com/cart.htm").MustWaitLoad() c.page.MustWaitIdle() log.Println("[购物车] 页面加载完成") return nil } // Refresh 刷新购物车页面 func (c *Cart) Refresh() error { log.Println("[购物车] 刷新购物车...") c.page.MustReload().MustWaitLoad() c.page.MustWaitIdle() log.Println("[购物车] 刷新完成") return nil } // SelectAll 全选购物车商品 func (c *Cart) SelectAll() error { log.Println("[购物车] 尝试全选商品...") // 淘宝购物车全选按钮,尝试多种选择器兼容 selectors := []string{ // 常见的全选 checkbox 选择器 `#cart-operation-fixed > div.cartOperationLeft--JnN5kBCf > label > span.ant-checkbox.ant-checkbox-checked > input[type="checkbox"]`, } for _, sel := range selectors { el, err := c.page.Timeout(3 * time.Second).Element(sel) if err == nil && el != nil { el.MustClick() log.Printf("[购物车] 已点击全选按钮: %s", sel) time.Sleep(500 * time.Millisecond) return nil } } // 兜底方案:通过 JS 全选 log.Println("[购物车] CSS 选择器未匹配,尝试 JS 全选...") c.page.MustEval(`() => { // 查找所有未选中的 checkbox 并点击 const checkboxes = document.querySelectorAll('input[type="checkbox"]:not(:checked)'); checkboxes.forEach(cb => cb.click()); }`) return nil } // ClickCheckout 点击"结算"按钮 func (c *Cart) ClickCheckout() error { log.Println("[购物车] 查找结算按钮...") // 按稳定性排序: class 选择器 > 容器内查找 > 文本匹配 selectors := []string{ "div.trade-cart-btn-submit div.btn--QDjHtErD", // 精确 class 路径 "div.btnWrap--hJoXF9T5 div.btn--QDjHtErD", // 备用 wrapper class "#settlementContainer_1 div.btn--QDjHtErD", // 容器 ID + class "div[class*='trade-cart-btn-submit'] div[class*='btn--']", // 模糊匹配 } for _, sel := range selectors { el, err := c.page.Timeout(3 * time.Second).Element(sel) if err == nil && el != nil { el.MustClick() log.Printf("[购物车] 已点击结算按钮: %s", sel) return nil } } // 兆底: 通过文本内容查找结算按钮 log.Println("[购物车] 尝试通过文本内容查找结算按钮...") el, err := c.page.Timeout(3*time.Second).ElementR("div", "结算") if err == nil && el != nil { el.MustClick() log.Println("[购物车] 已通过文本匹配点击结算按钮") return nil } return fmt.Errorf("未找到结算按钮") } // HoverCheckout 将鼠标悬停在结算按钮上(预热,减少点击延迟) func (c *Cart) HoverCheckout() { log.Println("[购物车] 预热:悬停结算按钮...") selectors := []string{ "div.trade-cart-btn-submit div.btn--QDjHtErD", "div.btnWrap--hJoXF9T5 div.btn--QDjHtErD", "#settlementContainer_1 div.btn--QDjHtErD", } for _, sel := range selectors { el, err := c.page.Timeout(2 * time.Second).Element(sel) if err == nil && el != nil { el.MustHover() log.Printf("[购物车] 已悬停结算按钮: %s", sel) return } } log.Println("[购物车] 预热:未找到结算按钮") } // DisableAnimations 注入 JS 禁用页面动画,加速交互 func (c *Cart) DisableAnimations() { c.page.MustEval(`() => { const style = document.createElement('style'); style.textContent = '*, *::before, *::after { transition: none !important; animation: none !important; }'; document.head.appendChild(style); }`) log.Println("[购物车] 已禁用页面动画") }