Files
yidaima_tools/step1.py
王鹏 a2f5875d1b init
2026-04-09 14:55:54 +08:00

211 lines
8.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import os
import re
import time
from typing import Optional, Callable
from config_loader import get_config
def _now_datestr() -> str:
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d")
def _safe_screenshot(page, save_path: str) -> None:
try:
page.screenshot(path=save_path, full_page=True)
except Exception:
pass
class Step1FeastCoding:
"""
Step 1: 访问 feast.yidaima.cn登录并点击 FeastCoding 按钮
参考: docs/step1.md
"""
def __init__(self, chrome_path: str, username: str, password: str, log_callback: Optional[Callable[[str], None]] = None):
self.chrome_path = chrome_path
self.username = username
self.password = password
self.url = "https://feast.yidaima.cn/login"
# self.url = "http://localhost/login"
self.project_name = "【A173】基于Springboot + vue3实现的学生交流互助平台"
self.feast_button = "FeastCoding"
self.browser = None
self.page = None
self.log_callback = log_callback
def _log(self, message: str):
"""输出日志,支持回调到 GUI"""
print(message)
if self.log_callback:
self.log_callback(message)
def run(self) -> bool:
"""执行 Step 1 流程,成功返回 True"""
from playwright.sync_api import sync_playwright
save_dir = os.path.join(".", "data", "screenshots", _now_datestr())
os.makedirs(save_dir, exist_ok=True)
err_path = os.path.join(save_dir, "step1_error.png")
try:
with sync_playwright() as p:
self.browser = p.chromium.launch(headless=False, executable_path=self.chrome_path)
context = self.browser.new_context(viewport={"width": 1280, "height": 800})
self.page = context.new_page()
# 1) 登录页
self._log("[Step1] 正在打开登录页...")
self.page.goto(self.url, wait_until="networkidle")
self.page.wait_for_timeout(2000)
# 等待输入框出现
self.page.wait_for_selector("input[type='text'], input[name*='user' i], input[placeholder*='' i]", timeout=15000)
# 查找用户名和密码输入框
user_candidates = [
self.page.locator("input[type='text']").first,
self.page.locator("input[name*='user' i]").first,
self.page.locator("input[name*='username' i]").first,
self.page.locator("input[placeholder*='' i]").first,
self.page.get_by_placeholder(re.compile(r"用户名|账号|登录名", re.I)).first,
]
pass_candidates = [
self.page.locator("input[type='password']").first,
self.page.locator("input[name*='pass' i]").first,
self.page.locator("input[placeholder*='' i]").first,
self.page.get_by_placeholder(re.compile(r"密码", re.I)).first,
]
user_loc = next((c for c in user_candidates if c.count() > 0 and c.is_visible()), None)
pass_loc = next((c for c in pass_candidates if c.count() > 0 and c.is_visible()), None)
if user_loc is None or pass_loc is None:
raise RuntimeError("未能定位到用户名/密码输入框。")
# 输入用户名密码
self._log("[Step1] 正在输入用户名密码...")
user_loc.click(timeout=5000, force=True)
user_loc.fill(self.username)
pass_loc.click(timeout=5000, force=True)
pass_loc.fill(self.password)
# 登录按钮
login_btn = self.page.get_by_role("button", name=re.compile(r"(登录|Log in|Sign in|立即登录)", re.I)).first
try:
if login_btn.count() > 0:
login_btn.click(timeout=10000, force=True)
else:
self.page.locator("button[type='submit']").first.click(timeout=10000, force=True)
except Exception:
self.page.keyboard.press("Enter")
# 等待菜单
self._log("[Step1] 等待登录完成...")
self.page.wait_for_selector("text=工作室运营", timeout=30000)
time.sleep(1)
# 2) 进入源码管理
self._log("[Step1] 进入源码管理...")
self.page.get_by_text("工作室运营", exact=False).first.click(timeout=10000, force=True)
time.sleep(0.8)
self.page.get_by_text("源码管理", exact=False).first.click(timeout=10000, force=True)
time.sleep(1)
# 3) 搜索项目
self._log("[Step1] 搜索项目...")
self.page.wait_for_timeout(1500)
search_input_candidates = [
self.page.locator("input[type='search']").first,
self.page.locator("input[placeholder*='搜索' i]").first,
self.page.get_by_placeholder("搜索").first,
self.page.locator(".el-input__inner").filter(has_text=re.compile(r"搜索", re.I)).first,
]
search_input = next((c for c in search_input_candidates if c.count() > 0 and c.is_visible()), None)
if search_input is None:
all_inputs = self.page.locator("input[type='text']").all()
for inp in all_inputs:
if inp.is_visible():
search_input = inp
break
if search_input is None:
raise RuntimeError("未能定位到搜索输入框。")
search_input.click(timeout=5000, force=True)
search_input.fill(self.project_name)
search_input.press("Enter")
# 从 project_name 提取项目编号前6个字符
project_code = self.project_name[:6] if len(self.project_name) >= 6 else self.project_name
# 等待列表渲染
self.page.wait_for_selector(f"text={project_code}", timeout=20000)
self.page.wait_for_timeout(1000)
# 查找项目行
row = self.page.locator("tr", has_text=self.project_name).first
if row.count() == 0:
row = self.page.locator("tr", has_text=project_code).first
# 点击"查看"按钮
view_btn = row.get_by_role("button", name=re.compile(r"(查看|View)", re.I)).first
if view_btn.count() == 0 or not view_btn.is_visible():
raise RuntimeError("未能定位到'查看'按钮。")
view_btn.click(timeout=10000)
# 4) 滚动到底并点击 FeastCoding
self._log("[Step1] 查找 FeastCoding 按钮...")
self.page.wait_for_timeout(1500)
feast_loc = self.page.get_by_text(self.feast_button, exact=False).first
feast_loc.wait_for(state="visible", timeout=15000)
self._log("[Step1] 已找到 FeastCoding 按钮")
feast_loc.scroll_into_view_if_needed(timeout=10000)
self.page.wait_for_timeout(800)
self._log("[Step1] 点击 FeastCoding 按钮...")
feast_loc.click(timeout=10000)
self.page.wait_for_timeout(3000)
self._log("[Step1] 已完成,内容已复制到剪贴板")
return True
except Exception as e:
if self.page is not None:
_safe_screenshot(self.page, err_path)
raise e
finally:
if self.browser is not None:
try:
self.browser.close()
except Exception:
pass
def main():
"""单独运行 Step1"""
config = get_config()
chrome_path = config.chrome_path
step1_config = config.step1_config
username = step1_config.get("username", "wangpeng")
password = step1_config.get("password", "Feastcoding@123")
step1 = Step1FeastCoding(chrome_path, username, password)
try:
step1.run()
print("[INFO] Step1 执行成功!")
except Exception as e:
print(f"[ERROR] Step1 执行失败: {e}")
if __name__ == "__main__":
main()