第一版

This commit is contained in:
zyj
2026-02-09 16:05:56 +08:00
parent fcdc5b8f2d
commit 514d7f9b0e
11 changed files with 1137 additions and 365 deletions

132
services/cart.go Normal file
View File

@@ -0,0 +1,132 @@
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("[购物车] 已禁用页面动画")
}