2024-11-23 14:27:50 +00:00
|
|
|
|
import sqlite3
|
|
|
|
|
import pandas as pd
|
|
|
|
|
from contextlib import contextmanager
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
class AccountData:
|
|
|
|
|
email: str # 邮箱
|
|
|
|
|
original_password: str # 原密码
|
|
|
|
|
original_aux_email: str # 原辅助邮箱
|
|
|
|
|
new_password: str # 新密码
|
|
|
|
|
new_aux_email: str # 新辅助邮箱
|
|
|
|
|
change_status: str # 是否更改完成
|
2024-11-26 16:30:00 +00:00
|
|
|
|
proxy: str # 代理
|
|
|
|
|
region: Optional[str] = None # 区域(可选)
|
2024-11-23 14:27:50 +00:00
|
|
|
|
|
|
|
|
|
class AccountManagerSQLite:
|
2024-11-26 16:30:00 +00:00
|
|
|
|
def __init__(self, db_path="accounts.db", debug=False):
|
2024-11-23 14:27:50 +00:00
|
|
|
|
self.db_path = db_path
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug = debug
|
2024-11-23 14:27:50 +00:00
|
|
|
|
self._initialize_db()
|
|
|
|
|
|
2024-11-26 16:30:00 +00:00
|
|
|
|
def debug_print(self, *args):
|
|
|
|
|
"""仅在 debug 模式下输出调试信息"""
|
|
|
|
|
if self.debug:
|
|
|
|
|
print(*args)
|
|
|
|
|
|
2024-11-23 14:27:50 +00:00
|
|
|
|
def _initialize_db(self):
|
2024-11-26 16:30:00 +00:00
|
|
|
|
"""初始化或检查数据库结构"""
|
2024-11-23 14:27:50 +00:00
|
|
|
|
with self._get_connection() as conn:
|
2024-11-26 16:30:00 +00:00
|
|
|
|
# 启用 WAL 模式
|
|
|
|
|
current_mode = conn.execute("PRAGMA journal_mode").fetchone()[0]
|
|
|
|
|
if current_mode != "wal":
|
|
|
|
|
self.debug_print("切换到 WAL 模式...")
|
|
|
|
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
|
|
|
|
|
|
|
|
# 检查表是否存在
|
|
|
|
|
cursor = conn.execute("PRAGMA table_info(accounts)")
|
|
|
|
|
columns = [row[1] for row in cursor.fetchall()]
|
|
|
|
|
|
|
|
|
|
# 定义表结构
|
|
|
|
|
required_columns = [
|
|
|
|
|
"email", "original_password", "original_aux_email",
|
|
|
|
|
"new_password", "new_aux_email", "change_status", "proxy", "region"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if not columns: # 表不存在
|
|
|
|
|
self.debug_print("表不存在,正在创建表...")
|
|
|
|
|
elif columns != required_columns: # 表存在但结构不一致
|
|
|
|
|
self.debug_print(f"表结构不一致,当前列: {columns}, 期望列: {required_columns}")
|
|
|
|
|
self.debug_print("正在重建表...")
|
|
|
|
|
conn.execute("DROP TABLE IF EXISTS accounts")
|
|
|
|
|
else: # 表存在且结构一致
|
|
|
|
|
self.debug_print("表结构检查通过,无需更改。")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 创建表
|
2024-11-23 14:27:50 +00:00
|
|
|
|
conn.execute("""
|
2024-11-26 16:30:00 +00:00
|
|
|
|
CREATE TABLE accounts (
|
2024-11-23 14:27:50 +00:00
|
|
|
|
email TEXT PRIMARY KEY,
|
|
|
|
|
original_password TEXT,
|
|
|
|
|
original_aux_email TEXT,
|
|
|
|
|
new_password TEXT,
|
|
|
|
|
new_aux_email TEXT,
|
2024-11-26 16:30:00 +00:00
|
|
|
|
change_status TEXT,
|
|
|
|
|
proxy TEXT,
|
|
|
|
|
region TEXT
|
2024-11-23 14:27:50 +00:00
|
|
|
|
)
|
|
|
|
|
""")
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print("表已创建。")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def _get_connection(self):
|
|
|
|
|
"""获取 SQLite 数据库连接"""
|
|
|
|
|
conn = sqlite3.connect(self.db_path)
|
|
|
|
|
try:
|
|
|
|
|
yield conn
|
|
|
|
|
finally:
|
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
|
"""清空数据库中的所有数据"""
|
|
|
|
|
with self._get_connection() as conn:
|
|
|
|
|
try:
|
|
|
|
|
conn.execute("DELETE FROM accounts")
|
|
|
|
|
conn.commit()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print("数据库已清空。")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
except sqlite3.Error as e:
|
|
|
|
|
conn.rollback()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"清空数据库失败:{e}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
def import_data(self, account_list: List[AccountData]):
|
|
|
|
|
"""批量导入数据"""
|
|
|
|
|
with self._get_connection() as conn:
|
|
|
|
|
try:
|
|
|
|
|
conn.executemany("""
|
|
|
|
|
INSERT OR REPLACE INTO accounts (
|
|
|
|
|
email, original_password, original_aux_email,
|
2024-11-26 16:30:00 +00:00
|
|
|
|
new_password, new_aux_email, change_status, proxy, region
|
|
|
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
2024-11-23 14:27:50 +00:00
|
|
|
|
""", [
|
|
|
|
|
(
|
|
|
|
|
account.email, account.original_password, account.original_aux_email,
|
2024-11-26 16:30:00 +00:00
|
|
|
|
account.new_password, account.new_aux_email, account.change_status,
|
|
|
|
|
account.proxy, account.region
|
2024-11-23 14:27:50 +00:00
|
|
|
|
) for account in account_list
|
|
|
|
|
])
|
|
|
|
|
conn.commit()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"成功导入 {len(account_list)} 条数据!")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
except sqlite3.Error as e:
|
|
|
|
|
conn.rollback()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"Error importing data: {e}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
def export_data(self) -> List[AccountData]:
|
|
|
|
|
"""导出所有数据"""
|
|
|
|
|
with self._get_connection() as conn:
|
|
|
|
|
cursor = conn.execute("SELECT * FROM accounts")
|
|
|
|
|
rows = cursor.fetchall()
|
|
|
|
|
return [AccountData(*row) for row in rows]
|
|
|
|
|
|
|
|
|
|
def query(self, **kwargs) -> List[AccountData]:
|
|
|
|
|
"""查询数据"""
|
|
|
|
|
query = "SELECT * FROM accounts WHERE " + " AND ".join([f"{key} = ?" for key in kwargs])
|
|
|
|
|
values = tuple(kwargs.values())
|
|
|
|
|
|
|
|
|
|
with self._get_connection() as conn:
|
|
|
|
|
cursor = conn.execute(query, values)
|
|
|
|
|
rows = cursor.fetchall()
|
|
|
|
|
return [AccountData(*row) for row in rows]
|
|
|
|
|
|
|
|
|
|
def update_record(self, email: str, **kwargs):
|
2024-11-26 16:30:00 +00:00
|
|
|
|
"""更新记录的指定字段"""
|
2024-11-23 14:27:50 +00:00
|
|
|
|
if not kwargs:
|
|
|
|
|
raise ValueError("没有指定任何更新的字段")
|
|
|
|
|
|
|
|
|
|
# 动态生成 SQL 的 SET 子句
|
|
|
|
|
set_clause = ", ".join([f"{key} = ?" for key in kwargs.keys()])
|
|
|
|
|
values = list(kwargs.values())
|
|
|
|
|
values.append(email) # 将 email 添加到参数列表的最后
|
|
|
|
|
|
|
|
|
|
query = f"""
|
|
|
|
|
UPDATE accounts
|
|
|
|
|
SET {set_clause}
|
|
|
|
|
WHERE email = ?
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
with self._get_connection() as conn:
|
|
|
|
|
try:
|
|
|
|
|
conn.execute(query, values)
|
|
|
|
|
conn.commit()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"成功更新记录: {email}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
except sqlite3.Error as e:
|
|
|
|
|
conn.rollback()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"更新记录失败:{e}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
def delete_account(self, email: str):
|
|
|
|
|
"""删除某个账户"""
|
|
|
|
|
with self._get_connection() as conn:
|
|
|
|
|
try:
|
|
|
|
|
conn.execute("DELETE FROM accounts WHERE email = ?", (email,))
|
|
|
|
|
conn.commit()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"成功删除账户: {email}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
except sqlite3.Error as e:
|
|
|
|
|
conn.rollback()
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"Error deleting account: {e}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
def import_from_excel(self, excel_path: str, clear_old: bool = False):
|
2024-11-26 16:30:00 +00:00
|
|
|
|
"""从 Excel 文件导入数据"""
|
2024-11-23 14:27:50 +00:00
|
|
|
|
try:
|
|
|
|
|
if clear_old:
|
|
|
|
|
self.clear()
|
|
|
|
|
|
2024-11-26 16:30:00 +00:00
|
|
|
|
# 读取 Excel 文件
|
2024-11-23 14:27:50 +00:00
|
|
|
|
df = pd.read_excel(excel_path, sheet_name=0)
|
|
|
|
|
|
|
|
|
|
# 校验表格格式
|
2024-11-26 16:30:00 +00:00
|
|
|
|
required_columns = [
|
|
|
|
|
"邮箱", "原密码", "原辅助邮箱", "新密码", "新辅助邮箱", "是否更改完成", "代理"
|
|
|
|
|
]
|
|
|
|
|
optional_columns = ["区域"]
|
|
|
|
|
all_columns = required_columns + optional_columns
|
|
|
|
|
|
|
|
|
|
missing_columns = [col for col in required_columns if col not in df.columns]
|
|
|
|
|
if missing_columns:
|
|
|
|
|
raise ValueError(f"表格缺少必要的列:{missing_columns}")
|
|
|
|
|
|
|
|
|
|
# 添加缺失的可选列并填充默认值
|
|
|
|
|
for col in optional_columns:
|
|
|
|
|
if col not in df.columns:
|
|
|
|
|
df[col] = None
|
2024-11-23 14:27:50 +00:00
|
|
|
|
|
2024-11-26 16:30:00 +00:00
|
|
|
|
# 转换数据为 AccountData 对象
|
2024-11-23 14:27:50 +00:00
|
|
|
|
account_list = [
|
|
|
|
|
AccountData(
|
|
|
|
|
email=row["邮箱"],
|
|
|
|
|
original_password=row["原密码"],
|
|
|
|
|
original_aux_email=row["原辅助邮箱"],
|
|
|
|
|
new_password=row["新密码"],
|
|
|
|
|
new_aux_email=row["新辅助邮箱"],
|
2024-11-26 16:30:00 +00:00
|
|
|
|
change_status=row["是否更改完成"],
|
|
|
|
|
proxy=row["代理"],
|
|
|
|
|
region=row.get("区域", None)
|
2024-11-23 14:27:50 +00:00
|
|
|
|
)
|
|
|
|
|
for _, row in df.iterrows()
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
self.import_data(account_list)
|
|
|
|
|
except Exception as e:
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"导入失败:{e}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
def export_to_excel(self, excel_path: str):
|
2024-11-26 16:30:00 +00:00
|
|
|
|
"""导出数据到 Excel 文件"""
|
2024-11-23 14:27:50 +00:00
|
|
|
|
try:
|
|
|
|
|
accounts = self.export_data()
|
|
|
|
|
df = pd.DataFrame([{
|
|
|
|
|
"邮箱": account.email,
|
|
|
|
|
"原密码": account.original_password,
|
|
|
|
|
"原辅助邮箱": account.original_aux_email,
|
|
|
|
|
"新密码": account.new_password,
|
|
|
|
|
"新辅助邮箱": account.new_aux_email,
|
2024-11-26 16:30:00 +00:00
|
|
|
|
"是否更改完成": account.change_status,
|
|
|
|
|
"代理": account.proxy,
|
|
|
|
|
"区域": account.region
|
2024-11-23 14:27:50 +00:00
|
|
|
|
} for account in accounts])
|
|
|
|
|
|
|
|
|
|
df.to_excel(excel_path, index=False, sheet_name="Accounts")
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"成功导出数据到 {excel_path}!")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
except Exception as e:
|
2024-11-26 16:30:00 +00:00
|
|
|
|
self.debug_print(f"导出失败:{e}")
|
2024-11-23 14:27:50 +00:00
|
|
|
|
raise
|