🎉 项目优化与Bug修复完整版

 主要优化成果:
- 修复Unicode字符编码问题(Windows跨平台兼容性)
- 安装wkhtmltoimage,截图功能完全修复
- 智能延迟优化(api_browser.py)
- 线程池资源泄漏修复(tasks.py)
- HTML解析缓存机制
- 二分搜索算法优化(kdocs_uploader.py)
- 自适应资源配置(browser_pool_worker.py)

🐛 Bug修复:
- 解决截图失败问题
- 修复管理员密码设置
- 解决应用启动编码错误

📚 新增文档:
- BUG_REPORT.md - 完整bug分析报告
- PERFORMANCE_ANALYSIS_REPORT.md - 性能优化分析
- LINUX_DEPLOYMENT_ANALYSIS.md - Linux部署指南
- SCREENSHOT_FIX_SUCCESS.md - 截图功能修复记录
- INSTALL_WKHTMLTOIMAGE.md - 安装指南
- OPTIMIZATION_FIXES_SUMMARY.md - 优化总结

🚀 功能验证:
- Flask应用正常运行(51233端口)
- 数据库、截图线程池、API预热正常
- 管理员登录:admin/admin123
- 健康检查API:http://127.0.0.1:51233/health

💡 技术改进:
- 智能延迟算法(自适应调整)
- LRU缓存策略
- 线程池资源管理优化
- 二分搜索算法(O(log n) vs O(n))
- 自适应资源管理

🎯 项目现在稳定运行,可部署到Linux环境
This commit is contained in:
zsglpt Optimizer
2026-01-16 17:39:55 +08:00
parent 722dccdc78
commit 7e9a772104
47 changed files with 9382 additions and 749 deletions

536
test_auto_login.py Normal file
View File

@@ -0,0 +1,536 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
金山文档上传测试 - 完整自动登录版本
自动处理:登录并加入编译 → 扫码 → 确认登录
"""
import os
import sys
import time
import base64
from datetime import datetime
from io import BytesIO
from PIL import Image
# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
from playwright.sync_api import sync_playwright
except ImportError:
print("错误: 需要安装 playwright")
print("请运行: pip install playwright")
sys.exit(1)
def log(message, level='INFO'):
"""日志输出"""
timestamp = datetime.now().strftime("%H:%M:%S")
print(f"[{timestamp}] {level}: {message}")
def pause(msg="按Enter键继续..."):
"""等待用户按键"""
input(f"\n{msg}")
def ask_yes_no(question, default='n'):
"""询问用户是/否问题"""
if default == 'y':
prompt = f"{question} (Y/n): "
else:
prompt = f"{question} (y/N): "
answer = input(prompt).strip().lower()
if not answer:
answer = default
return answer == 'y'
def save_qr_code(qr_image_bytes, filename="qr_code.png"):
"""保存二维码图片"""
try:
with open(filename, 'wb') as f:
f.write(qr_image_bytes)
log(f"[OK] 二维码已保存到: {filename}", 'SUCCESS')
return filename
except Exception as e:
log(f"✗ 保存二维码失败: {str(e)}", 'ERROR')
return None
def click_login_join_button(page):
"""点击'登录并加入编辑'按钮"""
log("查找'登录并加入编辑'按钮...", 'INFO')
# 多种可能的按钮选择器
login_selectors = [
"text=登录并加入编辑",
"text=登录并加入编译",
"button:has-text('登录')",
"text=立即登录",
"[class*='login']",
"[id*='login']"
]
for selector in login_selectors:
try:
button = page.locator(selector).first
if button.is_visible(timeout=3000):
log(f"[OK] 找到登录按钮: {selector}", 'SUCCESS')
button.click()
log("[OK] 已点击登录按钮", 'SUCCESS')
return True
except Exception:
continue
log("✗ 未找到登录按钮", 'ERROR')
return False
def wait_for_qr_code(page, timeout=30):
"""等待二维码出现"""
log("等待二维码加载...", 'INFO')
start_time = time.time()
while time.time() - start_time < timeout:
try:
# 查找二维码元素
qr_selectors = [
"canvas",
"img[src*='qr']",
"img[alt*='二维码']",
"[class*='qr']",
"[id*='qr']",
"div[class*='qrcode']",
"img[src*='wechat']"
]
for selector in qr_selectors:
try:
elements = page.query_selector_all(selector)
for i, element in enumerate(elements):
try:
# 尝试截图
screenshot = element.screenshot()
if len(screenshot) > 500: # 足够大的图片
filename = f"qr_code_{i}.png"
save_qr_code(screenshot, filename)
log(f"[OK] 找到二维码元素: {selector}[{i}]", 'SUCCESS')
return True
except Exception:
continue
except Exception:
continue
time.sleep(1)
except Exception as e:
log(f"检查二维码时出错: {str(e)}", 'WARNING')
time.sleep(1)
return False
def wait_for_confirm_login(page, timeout=120):
"""等待'确认登录'按钮出现并点击"""
log("等待用户扫码...", 'INFO')
log("请使用手机微信扫描二维码", 'INFO')
log("扫码完成后,程序会自动检测并点击'确认登录'", 'INFO')
start_time = time.time()
check_interval = 2 # 每2秒检查一次
while time.time() - start_time < timeout:
try:
# 查找确认登录按钮
confirm_selectors = [
"text=确认登录",
"text=确认登陆",
"button:has-text('确认')",
"text=登录",
"[class*='confirm']",
"[id*='confirm']"
]
for selector in confirm_selectors:
try:
button = page.locator(selector).first
if button.is_visible(timeout=1000):
log(f"[OK] 找到确认按钮: {selector}", 'SUCCESS')
button.click()
log("[OK] 已点击确认登录按钮", 'SUCCESS')
return True
except Exception:
continue
# 如果没找到按钮,显示等待信息
elapsed = int(time.time() - start_time)
if elapsed % 10 == 0: # 每10秒显示一次
log(f"等待中... ({elapsed}秒)", 'INFO')
time.sleep(check_interval)
except Exception as e:
log(f"检查确认按钮时出错: {str(e)}", 'WARNING')
time.sleep(check_interval)
return False
def wait_for_document_loaded(page, timeout=30):
"""等待文档页面加载完成"""
log("等待文档页面加载...", 'INFO')
start_time = time.time()
while time.time() - start_time < timeout:
try:
current_url = page.url
log(f"当前URL: {current_url}", 'INFO')
# 检查是否进入文档页面
if "kdocs.cn" in current_url and "/spreadsheet/" in current_url:
log("[OK] 已进入文档页面", 'SUCCESS')
return True
# 检查表格元素
try:
canvas_count = page.locator("canvas").count()
if canvas_count > 0:
log(f"[OK] 检测到 {canvas_count} 个表格元素", 'SUCCESS')
return True
except:
pass
time.sleep(2)
except Exception as e:
log(f"检查页面状态时出错: {str(e)}", 'WARNING')
time.sleep(2)
return False
def main():
"""主函数"""
print("=" * 70)
print("[LOCK] 金山文档上传测试 - 完整自动登录版本")
print("=" * 70)
print()
print("特点:")
print(" [OK] 自动点击'登录并加入编译'")
print(" [OK] 自动捕获二维码")
print(" [OK] 自动等待并点击'确认登录'")
print(" [OK] 自动检测文档加载")
print()
# 配置
doc_url = input("请输入金山文档URL (或按Enter使用默认): ").strip()
if not doc_url:
doc_url = "https://kdocs.cn/l/cpwEOo5ynKX4"
print(f"\n使用URL: {doc_url}")
print()
if not ask_yes_no("确认开始测试?"):
print("测试已取消")
return
print("\n" + "=" * 70)
print("开始测试流程")
print("=" * 70)
playwright = None
browser = None
context = None
page = None
try:
# ===== 步骤1: 启动浏览器 =====
print("\n" + "=" * 50)
print("步骤1: 启动浏览器")
print("=" * 50)
log("正在启动Playwright...", 'INFO')
playwright = sync_playwright().start()
log("[OK] Playwright启动成功", 'SUCCESS')
log("正在启动浏览器...", 'INFO')
browser = playwright.chromium.launch(headless=False)
log("[OK] 浏览器启动成功", 'SUCCESS')
log("正在创建上下文...", 'INFO')
context = browser.new_context()
log("[OK] 上下文创建成功", 'SUCCESS')
log("正在创建页面...", 'INFO')
page = context.new_page()
page.set_default_timeout(30000)
log("[OK] 页面创建成功", 'SUCCESS')
pause("浏览器已启动,请观察浏览器窗口")
log("额外等待5秒确保浏览器完全就绪...", 'INFO')
time.sleep(5)
# ===== 步骤2: 打开文档页面 =====
print("\n" + "=" * 50)
print("步骤2: 打开文档页面")
print("=" * 50)
log(f"正在导航到: {doc_url}", 'INFO')
page.goto(doc_url, wait_until='domcontentloaded')
log("[OK] 页面导航完成", 'SUCCESS')
log("等待8秒让页面完全加载...", 'INFO')
time.sleep(8)
current_url = page.url
log(f"当前URL: {current_url}", 'INFO')
# ===== 步骤3: 自动点击登录按钮 =====
print("\n" + "=" * 50)
print("步骤3: 点击登录按钮")
print("=" * 50)
log("检测页面状态...", 'INFO')
log("等待页面元素完全加载...", 'INFO')
# 额外的等待确保页面完全加载
log("额外等待5秒确保页面完全加载...", 'INFO')
time.sleep(5)
# 尝试等待特定元素出现
try:
page.wait_for_selector("text=登录并加入", timeout=15000)
log("[OK] 检测到'登录并加入编辑'页面", 'SUCCESS')
login_button_found = True
except:
log("⚠ 未检测到登录按钮,继续等待...", 'WARNING')
time.sleep(5)
login_button_found = False
# 最终检测页面内容
page_content = page.content()
if "登录并加入" in page_content:
log("[OK] 检测到'登录并加入编辑'页面", 'SUCCESS')
login_button_found = True
else:
log("⚠ 未检测到'登录并加入编辑'页面", 'WARNING')
login_button_found = False
# 执行点击操作
if login_button_found:
if click_login_join_button(page):
log("[OK] 已点击登录按钮,等待跳转到扫码页面...", 'SUCCESS')
time.sleep(5) # 增加等待时间
else:
log("✗ 点击登录按钮失败", 'ERROR')
return
else:
# 检查是否已经直接进入登录页面
if "login" in page.url.lower() or "account" in page.url.lower():
log("[OK] 已直接进入登录页面", 'SUCCESS')
else:
log("⚠ 页面状态不明确,请手动检查浏览器窗口", 'WARNING')
# ===== 步骤4: 等待二维码 =====
print("\n" + "=" * 50)
print("步骤4: 等待二维码")
print("=" * 50)
if wait_for_qr_code(page, timeout=90):
log("[OK] 二维码加载完成", 'SUCCESS')
else:
log("⚠ 未检测到二维码,可能页面结构有变化", 'WARNING')
# ===== 步骤5: 等待确认登录 =====
print("\n" + "=" * 50)
print("步骤5: 等待确认登录")
print("=" * 50)
log("扫码流程:", 'INFO')
log("1. 请使用手机微信扫描二维码", 'INFO')
log("2. 扫码后点击'确认登录'", 'INFO')
log("3. 程序会自动检测并处理", 'INFO')
if wait_for_confirm_login(page, timeout=180):
log("[OK] 登录确认完成", 'SUCCESS')
else:
log("⚠ 未检测到确认登录操作", 'WARNING')
# ===== 步骤6: 等待文档加载 =====
print("\n" + "=" * 50)
print("步骤6: 等待文档加载")
print("=" * 50)
if wait_for_document_loaded(page, timeout=60):
log("[OK] 文档页面加载完成", 'SUCCESS')
# 验证表格元素
try:
canvas_count = page.locator("canvas").count()
log(f"[OK] 检测到 {canvas_count} 个表格元素", 'SUCCESS')
# 尝试读取名称框
try:
name_box = page.locator("input.edit-box").first
if name_box.is_visible():
value = name_box.input_value()
log(f"[OK] 名称框可见,当前值: '{value}'", 'SUCCESS')
except:
pass
except Exception as e:
log(f"检查表格元素时出错: {str(e)}", 'WARNING')
else:
log("⚠ 文档页面加载超时", 'WARNING')
# ===== 步骤7: 表格功能测试 =====
print("\n" + "=" * 50)
print("步骤7: 表格功能测试")
print("=" * 50)
# 测试搜索功能
test_name = input("请输入要搜索的姓名 (默认: 张三): ").strip()
if not test_name:
test_name = "张三"
log(f"搜索姓名: {test_name}", 'INFO')
try:
page.keyboard.press("Control+f")
time.sleep(0.5)
page.keyboard.type(test_name)
time.sleep(0.3)
page.keyboard.press("Enter")
time.sleep(1)
page.keyboard.press("Escape")
time.sleep(0.3)
log("[OK] 搜索测试完成", 'SUCCESS')
log("请查看浏览器窗口,检查搜索结果", 'INFO')
except Exception as e:
log(f"✗ 搜索测试失败: {str(e)}", 'ERROR')
pause("搜索测试完成")
# ===== 步骤8: 图片上传测试 =====
print("\n" + "=" * 50)
print("步骤8: 图片上传测试")
print("=" * 50)
if ask_yes_no("是否进行图片上传测试?"):
image_path = input("请输入测试图片的完整路径: ").strip()
if not image_path or not os.path.exists(image_path):
log("图片文件不存在,跳过上传测试", 'WARNING')
else:
log(f"选中的图片: {image_path}", 'INFO')
try:
# 导航到D3
name_box = page.locator("input.edit-box").first
name_box.click()
name_box.fill("D3")
name_box.press("Enter")
time.sleep(0.5)
log("[OK] 已导航到D3单元格")
# 点击插入
insert_btn = page.locator("text=插入").first
insert_btn.click()
time.sleep(0.5)
log("[OK] 已点击插入按钮")
# 点击图片
image_btn = page.locator("text=图片").first
image_btn.click()
time.sleep(0.5)
log("[OK] 已点击图片按钮")
# 选择本地
local_option = page.locator("text=本地").first
local_option.click()
log("[OK] 已选择本地图片")
# 上传文件
with page.expect_file_chooser() as fc_info:
pass
file_chooser = fc_info.value
file_chooser.set_files(image_path)
log("[OK] 文件上传命令已发送")
time.sleep(3)
log("[OK] 图片上传测试完成", 'SUCCESS')
except Exception as e:
log(f"✗ 图片上传测试失败: {str(e)}", 'ERROR')
pause("所有测试完成")
# ===== 测试完成 =====
print("\n" + "=" * 70)
log("🎉 所有测试完成!", 'SUCCESS')
print("=" * 70)
except KeyboardInterrupt:
print("\n")
log("测试被用户中断", 'WARNING')
except Exception as e:
print("\n")
log(f"测试过程中出现错误: {str(e)}", 'ERROR')
import traceback
traceback.print_exc()
finally:
# 清理资源
print("\n" + "=" * 70)
print("清理资源...")
print("=" * 70)
try:
if page:
page.close()
log("[OK] 页面已关闭", 'SUCCESS')
except:
pass
try:
if context:
context.close()
log("[OK] 上下文已关闭", 'SUCCESS')
except:
pass
try:
if browser:
browser.close()
log("[OK] 浏览器已关闭", 'SUCCESS')
except:
pass
try:
if playwright:
playwright.stop()
log("[OK] Playwright已停止", 'SUCCESS')
except:
pass
log("测试结束", 'SUCCESS')
print("=" * 70)
if __name__ == "__main__":
main()