Files
zsglpt-pc/config.py
237899745 9743186a9e feat: 添加依赖自动检测与安装、选项记忆、KDocs登录优化
- 新增依赖检测模块:启动时自动检测wkhtmltoimage和Playwright Chromium
- 新增依赖安装对话框:缺失时提示用户一键下载安装
- 修复选项记忆功能:浏览类型、自动截图、自动上传选项现在会保存
- 优化KDocs登录检测:未登录时自动切换到金山文档页面并显示二维码
- 简化日志输出:移除debug信息,保留用户友好的状态提示
- 新增账号变化信号:账号管理页面的修改会自动同步到浏览任务页面

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 01:28:06 +08:00

251 lines
7.9 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.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配置管理模块 - 精简版
使用JSON本地存储不搞数据库那些花里胡哨的东西
"""
import os
from dataclasses import dataclass, field
from typing import List, Optional
from pathlib import Path
# 项目根目录
BASE_DIR = Path(__file__).parent.absolute()
DATA_DIR = BASE_DIR / "data"
COOKIES_DIR = DATA_DIR / "cookies"
SCREENSHOTS_DIR = DATA_DIR / "screenshots"
# 确保目录存在
DATA_DIR.mkdir(exist_ok=True)
COOKIES_DIR.mkdir(exist_ok=True)
SCREENSHOTS_DIR.mkdir(exist_ok=True)
# 配置文件路径
CONFIG_FILE = DATA_DIR / "config.json"
ENCRYPTION_KEY_FILE = DATA_DIR / "encryption_key.bin"
ENCRYPTION_SALT_FILE = DATA_DIR / "encryption_salt.bin"
KDOCS_LOGIN_STATE_FILE = DATA_DIR / "kdocs_login_state.json"
@dataclass
class AccountConfig:
"""账号配置"""
username: str
password: str # 加密存储
remark: str = ""
enabled: bool = True
@dataclass
class KDocsConfig:
"""金山文档配置"""
enabled: bool = False
doc_url: str = "https://kdocs.cn/l/cpwEOo5ynKX4"
sheet_name: str = "Sheet1"
sheet_index: int = 0
unit_column: str = "A"
image_column: str = "D"
unit: str = "" # 县区名
name_column: str = "C" # 姓名列
row_start: int = 0 # 有效行起始0表示不限制
row_end: int = 0 # 有效行结束0表示不限制
@dataclass
class ScreenshotConfig:
"""截图配置"""
dir: str = str(SCREENSHOTS_DIR)
quality: int = 95
width: int = 1920
height: int = 1080
js_delay_ms: int = 3000
timeout_seconds: int = 60
wkhtmltoimage_path: str = "" # 自定义路径,空则自动查找
@dataclass
class ProxyConfig:
"""代理配置"""
enabled: bool = False
server: str = "" # http://host:port
@dataclass
class ZSGLConfig:
"""知识管理平台配置"""
base_url: str = "https://postoa.aidunsoft.com"
login_url: str = "https://postoa.aidunsoft.com/admin/login.aspx"
index_url_pattern: str = "index.aspx"
@dataclass
class TaskConfig:
"""浏览任务配置"""
browse_type: str = "应读" # 浏览类型
auto_screenshot: bool = True # 浏览后自动截图
auto_upload: bool = False # 截图后自动上传
@dataclass
class AppConfig:
"""应用总配置"""
accounts: List[AccountConfig] = field(default_factory=list)
kdocs: KDocsConfig = field(default_factory=KDocsConfig)
screenshot: ScreenshotConfig = field(default_factory=ScreenshotConfig)
proxy: ProxyConfig = field(default_factory=ProxyConfig)
zsgl: ZSGLConfig = field(default_factory=ZSGLConfig)
task: TaskConfig = field(default_factory=TaskConfig)
theme: str = "light" # light/dark
def to_dict(self) -> dict:
"""转换为字典"""
return {
"accounts": [
{
"username": a.username,
"password": a.password,
"remark": a.remark,
"enabled": a.enabled
} for a in self.accounts
],
"kdocs": {
"enabled": self.kdocs.enabled,
"doc_url": self.kdocs.doc_url,
"sheet_name": self.kdocs.sheet_name,
"sheet_index": self.kdocs.sheet_index,
"unit_column": self.kdocs.unit_column,
"image_column": self.kdocs.image_column,
"unit": self.kdocs.unit,
"name_column": self.kdocs.name_column,
"row_start": self.kdocs.row_start,
"row_end": self.kdocs.row_end,
},
"screenshot": {
"dir": self.screenshot.dir,
"quality": self.screenshot.quality,
"width": self.screenshot.width,
"height": self.screenshot.height,
"js_delay_ms": self.screenshot.js_delay_ms,
"timeout_seconds": self.screenshot.timeout_seconds,
"wkhtmltoimage_path": self.screenshot.wkhtmltoimage_path,
},
"proxy": {
"enabled": self.proxy.enabled,
"server": self.proxy.server,
},
"zsgl": {
"base_url": self.zsgl.base_url,
"login_url": self.zsgl.login_url,
"index_url_pattern": self.zsgl.index_url_pattern,
},
"task": {
"browse_type": self.task.browse_type,
"auto_screenshot": self.task.auto_screenshot,
"auto_upload": self.task.auto_upload,
},
"theme": self.theme,
}
@classmethod
def from_dict(cls, data: dict) -> "AppConfig":
"""从字典创建配置"""
config = cls()
# 加载账号
accounts_data = data.get("accounts", [])
config.accounts = [
AccountConfig(
username=a.get("username", ""),
password=a.get("password", ""),
remark=a.get("remark", ""),
enabled=a.get("enabled", True)
) for a in accounts_data
]
# 加载金山文档配置
kdocs_data = data.get("kdocs", {})
config.kdocs = KDocsConfig(
enabled=kdocs_data.get("enabled", False),
doc_url=kdocs_data.get("doc_url", "https://kdocs.cn/l/cpwEOo5ynKX4"),
sheet_name=kdocs_data.get("sheet_name", "Sheet1"),
sheet_index=kdocs_data.get("sheet_index", 0),
unit_column=kdocs_data.get("unit_column", "A"),
image_column=kdocs_data.get("image_column", "D"),
unit=kdocs_data.get("unit", ""),
name_column=kdocs_data.get("name_column", "C"),
row_start=kdocs_data.get("row_start", 0),
row_end=kdocs_data.get("row_end", 0),
)
# 加载截图配置
screenshot_data = data.get("screenshot", {})
config.screenshot = ScreenshotConfig(
dir=screenshot_data.get("dir", str(SCREENSHOTS_DIR)),
quality=screenshot_data.get("quality", 95),
width=screenshot_data.get("width", 1920),
height=screenshot_data.get("height", 1080),
js_delay_ms=screenshot_data.get("js_delay_ms", 3000),
timeout_seconds=screenshot_data.get("timeout_seconds", 60),
wkhtmltoimage_path=screenshot_data.get("wkhtmltoimage_path", ""),
)
# 加载代理配置
proxy_data = data.get("proxy", {})
config.proxy = ProxyConfig(
enabled=proxy_data.get("enabled", False),
server=proxy_data.get("server", ""),
)
# 加载知识管理平台配置
zsgl_data = data.get("zsgl", {})
config.zsgl = ZSGLConfig(
base_url=zsgl_data.get("base_url", "https://postoa.aidunsoft.com"),
login_url=zsgl_data.get("login_url", "https://postoa.aidunsoft.com/admin/login.aspx"),
index_url_pattern=zsgl_data.get("index_url_pattern", "index.aspx"),
)
# 加载任务配置
task_data = data.get("task", {})
config.task = TaskConfig(
browse_type=task_data.get("browse_type", "应读"),
auto_screenshot=task_data.get("auto_screenshot", True),
auto_upload=task_data.get("auto_upload", False),
)
# 主题
config.theme = data.get("theme", "light")
return config
# 全局配置实例
_config: Optional[AppConfig] = None
def get_config() -> AppConfig:
"""获取配置实例(懒加载)"""
global _config
if _config is None:
from utils.storage import load_config
_config = load_config()
return _config
def save_config(config: AppConfig = None):
"""保存配置"""
global _config
if config:
_config = config
if _config:
from utils.storage import save_config as _save
_save(_config)
def reload_config():
"""重新加载配置"""
global _config
from utils.storage import load_config
_config = load_config()
return _config