133 lines
3.8 KiB
Go
133 lines
3.8 KiB
Go
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("[购物车] 已禁用页面动画")
|
|
}
|