package services import ( "fmt" "log" "strings" "time" "github.com/go-rod/rod" ) // OrderResult 下单结果 type OrderResult struct { Success bool Message string } // Order 订单提交服务 type Order struct { page *rod.Page } // NewOrder 创建订单服务 func NewOrder(page *rod.Page) *Order { return &Order{page: page} } // 提交订单按钮选择器列表 var submitSelectors = []string{ "#submitOrder > div > div.btnBox--p9CumEtE > div.btn--QDjHtErD", // 淘宝新版提交按钮 } // WaitForConfirmPage 等待订单确认页加载 func (o *Order) WaitForConfirmPage(timeout time.Duration) error { log.Println("[订单] 等待订单确认页加载...") deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { url := o.page.MustEval("() => window.location.href").String() if o.isConfirmPage(url) { log.Printf("[订单] 已进入订单确认页: %s", url) o.page.MustWaitLoad() time.Sleep(500 * time.Millisecond) return nil } time.Sleep(100 * time.Millisecond) } return fmt.Errorf("等待订单确认页超时") } // Refresh 刷新当前订单确认页 func (o *Order) Refresh() { log.Println("[订单] 刷新订单确认页...") o.page.MustReload().MustWaitLoad() o.page.MustWaitIdle() log.Println("[订单] 刷新完成") } // CanSubmit 检测当前订单页是否可以提交(按钮存在且未被禁用) func (o *Order) CanSubmit() bool { for _, sel := range submitSelectors { el, err := o.page.Timeout(1 * time.Second).Element(sel) if err != nil || el == nil { continue } // 检查按钮是否被禁用(class 含 disabled / 属性 disabled 等) className, _ := el.Attribute("class") if className != nil && strings.Contains(*className, "disabled") { log.Printf("[订单] 按钮存在但被禁用: %s", sel) return false } disabledAttr, _ := el.Attribute("disabled") if disabledAttr != nil { log.Printf("[订单] 按钮存在但 disabled 属性: %s", sel) return false } log.Printf("[订单] ✅ 按钮可点击: %s", sel) return true } // 还可以检查页面上是否有"该商品暂时无法购买"之类的提示 pageText := o.page.MustEval(`() => document.body.innerText`).String() if strings.Contains(pageText, "无法购买") || strings.Contains(pageText, "暂不可售") || strings.Contains(pageText, "已下架") || strings.Contains(pageText, "未开始") { log.Println("[订单] 页面提示商品暂时无法购买") return false } log.Println("[订单] 未找到提交按钮") return false } // RefreshUntilCanSubmit 循环刷新订单页,直到可以提交,返回时表示可以立即点击提交 func (o *Order) RefreshUntilCanSubmit(interval time.Duration, maxAttempts int) bool { for i := 1; i <= maxAttempts; i++ { log.Printf("[订单] 第 %d/%d 次检测是否可购买...", i, maxAttempts) if o.CanSubmit() { log.Println("[订单] ✅ 商品可购买!") return true } if i < maxAttempts { time.Sleep(interval) o.Refresh() } } log.Println("[订单] 已达最大检测次数,商品仍不可购买") return false } // Submit 点击"提交订单"按钮 func (o *Order) Submit() *OrderResult { log.Println("[订单] 点击提交订单...") for _, sel := range submitSelectors { el, err := o.page.Timeout(2 * time.Second).Element(sel) if err == nil && el != nil { el.MustClick() log.Printf("[订单] 已点击提交按钮: %s", sel) time.Sleep(1 * time.Second) return o.checkResult() } } return &OrderResult{ Success: false, Message: "未找到提交订单按钮", } } // checkResult 检查下单结果 func (o *Order) checkResult() *OrderResult { time.Sleep(2 * time.Second) url := o.page.MustEval("() => window.location.href").String() log.Printf("[订单] 提交后 URL: %s", url) if strings.Contains(url, "cashier") || strings.Contains(url, "pay") || strings.Contains(url, "alipay") { return &OrderResult{ Success: true, Message: "抢购成功!请尽快完成付款", } } return &OrderResult{ Success: false, Message: fmt.Sprintf("提交后仍在: %s", url), } } // isConfirmPage 判断 URL 是否为订单确认页 func (o *Order) isConfirmPage(url string) bool { return strings.Contains(url, "buy.taobao.com") || strings.Contains(url, "buyertrade") }