package publisher import ( "context" "fmt" "geo/internal/config" "log" "strings" "time" "github.com/go-rod/rod" ) type CSDNPublisher struct { *BasePublisher } func NewCSDNPublisher(ctx context.Context, task *TaskParams, cfg *config.Config, logger *log.Logger) PublisherInerface { return &CSDNPublisher{NewBasePublisher(ctx, task, cfg, logger)} } func (p *CSDNPublisher) CheckLogin() (bool, string) { p.LogInfo("检查登录状态...") if err := p.SetupDriver(); err != nil { return false, fmt.Sprintf("浏览器启动失败: %v", err) } defer p.Close() p.Page.MustNavigate(p.EditorURL) p.Sleep(2) p.WaitForPageReady(3) if p.CheckLoginStatus() { p.SaveCookies() return true, "已登录" } return false, "未登录" } func (p *CSDNPublisher) WaitLogin() (bool, string) { p.LogInfo("开始等待登录...") if err := p.SetupDriver(); err != nil { return false, fmt.Sprintf("浏览器启动失败: %v", err) } defer p.Close() p.Page.MustNavigate(p.EditorURL) p.Sleep(3) if p.CheckLoginStatus() { p.SaveCookies() p.LogInfo("已有登录状态") return true, "already_logged_in" } startTime := time.Now() timeout := 120 for time.Since(startTime) < time.Duration(timeout)*time.Second { time.Sleep(1 * time.Second) if p.CheckLoginStatus() { p.SaveCookies() p.LogInfo("登录成功") return true, "login_success" } p.SleepMs(1000) } return false, "登录超时,请检查网络或账号状态" } func (p *CSDNPublisher) inputTitle() error { p.LogInfo("输入文章标题...") titleInput, err := p.WaitForElementVisible("#txtTitle", 5) if err != nil { return fmt.Errorf("未找到标题输入框: %v", err) } p.LogInfo("找到标题输入框") if err := p.ClearInput(titleInput); err != nil { titleInput.Input("") } p.SleepMs(200) if err := p.SetInputValue(titleInput, p.Title); err != nil { titleInput.Input(p.Title) } p.LogInfo(fmt.Sprintf("标题已输入: %s", p.Title)) p.SleepMs(300) return nil } func (p *CSDNPublisher) inputContent(titleInput *rod.Element) error { p.LogInfo("输入文章内容...") p.SleepMs(1000) return nil } func (p *CSDNPublisher) clickPublish() error { p.LogInfo("点击发布按钮...") btnBox, err := p.WaitForElementVisible("div.btn-box", 3) if err != nil { return fmt.Errorf("未找到发布按钮区域: %v", err) } publishBtn, err := btnBox.ElementX(".//button[contains(@class, 'btn-outline-danger') and .//span[text()='发布博客']]") if err != nil { publishBtn, err = p.Page.ElementX("//button[contains(@class, 'btn-outline-danger') and .//span[text()='发布博客']]") if err != nil { return fmt.Errorf("未找到发布按钮: %v", err) } } if err := publishBtn.ScrollIntoView(); err != nil { p.LogInfo(fmt.Sprintf("滚动到发布按钮失败: %v", err)) } p.SleepMs(300) if err := p.JSClick(publishBtn); err != nil { return fmt.Errorf("点击发布按钮失败: %v", err) } p.LogInfo("已点击发布按钮") p.SleepMs(2000) return nil } func (p *CSDNPublisher) waitForPublishResult() (bool, string) { p.LogInfo("等待发布结果...") for attempt := 0; attempt < 10; attempt++ { currentURL := p.GetCurrentURL() p.LogInfo(fmt.Sprintf("第 %d 次检查 - URL: %s", attempt+1, currentURL)) if strings.Contains(currentURL, "success") { p.LogInfo(fmt.Sprintf("发布成功!URL: %s", currentURL)) return true, "发布成功" } errorSelectors := []string{ "[class*='el_mcm-message--error'] .el_mcm-message__content", "[class*='el_mcm-message--error']", "[class*='message--error']", ".error", ".alert-error", } for _, selector := range errorSelectors { errorElements, _ := p.Page.Elements(selector) for _, elem := range errorElements { visible, _ := elem.Visible() if visible { text, _ := elem.Text() if text != "" { p.LogError(fmt.Sprintf("发布失败: %s", text)) return false, fmt.Sprintf("发布失败: %s", text) } } } } // 截图保存用于调试 if attempt == 1 || attempt == 9 { screenshotPath := fmt.Sprintf("debug_%s_%d.png", p.RequestID, attempt) p.Screenshot(screenshotPath) p.LogInfo(fmt.Sprintf("已保存截图: %s", screenshotPath)) } p.SleepMs(1000) } return false, "发布结果未知" } func (p *CSDNPublisher) PublishNote() (bool, string) { p.StartNote() // 初始化浏览器 if err := p.SetupDriver(); err != nil { return false, fmt.Sprintf("浏览器启动失败: %v", err) } defer p.Close() // 执行发布流程 steps := []struct { name string fn func() error }{ {"初始化", p.InitPage}, {"保存cookie", p.SaveCookies}, {"输入标题", p.inputTitle}, {"输入内容", func() error { titleInput, _ := p.WaitForElementVisible("#txtTitle", 5) return p.inputContent(titleInput) }}, {"点击发布", p.clickPublish}, } for _, step := range steps { if err := step.fn(); err != nil { p.LogStep(step.name, false, err.Error()) return false, fmt.Sprintf("%s失败: %v", step.name, err) } p.LogStep(step.name, true, "") p.SleepMs(500) } // 等待发布结果 return p.waitForPublishResult() } func (p *CSDNPublisher) LogWarning(message string) { p.Logger.Printf("⚠️ %s", message) }