refactor: optimize structure, stability and runtime performance
This commit is contained in:
344
db/admin.py
344
db/admin.py
@@ -3,9 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytz
|
||||
|
||||
import db_pool
|
||||
from db.utils import get_cst_now_str
|
||||
@@ -16,6 +13,99 @@ from password_utils import (
|
||||
verify_password_sha256,
|
||||
)
|
||||
|
||||
_DEFAULT_SYSTEM_CONFIG = {
|
||||
"max_concurrent_global": 2,
|
||||
"max_concurrent_per_account": 1,
|
||||
"max_screenshot_concurrent": 3,
|
||||
"schedule_enabled": 0,
|
||||
"schedule_time": "02:00",
|
||||
"schedule_browse_type": "应读",
|
||||
"schedule_weekdays": "1,2,3,4,5,6,7",
|
||||
"proxy_enabled": 0,
|
||||
"proxy_api_url": "",
|
||||
"proxy_expire_minutes": 3,
|
||||
"enable_screenshot": 1,
|
||||
"auto_approve_enabled": 0,
|
||||
"auto_approve_hourly_limit": 10,
|
||||
"auto_approve_vip_days": 7,
|
||||
"kdocs_enabled": 0,
|
||||
"kdocs_doc_url": "",
|
||||
"kdocs_default_unit": "",
|
||||
"kdocs_sheet_name": "",
|
||||
"kdocs_sheet_index": 0,
|
||||
"kdocs_unit_column": "A",
|
||||
"kdocs_image_column": "D",
|
||||
"kdocs_admin_notify_enabled": 0,
|
||||
"kdocs_admin_notify_email": "",
|
||||
"kdocs_row_start": 0,
|
||||
"kdocs_row_end": 0,
|
||||
}
|
||||
|
||||
_SYSTEM_CONFIG_UPDATERS = (
|
||||
("max_concurrent_global", "max_concurrent"),
|
||||
("schedule_enabled", "schedule_enabled"),
|
||||
("schedule_time", "schedule_time"),
|
||||
("schedule_browse_type", "schedule_browse_type"),
|
||||
("schedule_weekdays", "schedule_weekdays"),
|
||||
("max_concurrent_per_account", "max_concurrent_per_account"),
|
||||
("max_screenshot_concurrent", "max_screenshot_concurrent"),
|
||||
("enable_screenshot", "enable_screenshot"),
|
||||
("proxy_enabled", "proxy_enabled"),
|
||||
("proxy_api_url", "proxy_api_url"),
|
||||
("proxy_expire_minutes", "proxy_expire_minutes"),
|
||||
("auto_approve_enabled", "auto_approve_enabled"),
|
||||
("auto_approve_hourly_limit", "auto_approve_hourly_limit"),
|
||||
("auto_approve_vip_days", "auto_approve_vip_days"),
|
||||
("kdocs_enabled", "kdocs_enabled"),
|
||||
("kdocs_doc_url", "kdocs_doc_url"),
|
||||
("kdocs_default_unit", "kdocs_default_unit"),
|
||||
("kdocs_sheet_name", "kdocs_sheet_name"),
|
||||
("kdocs_sheet_index", "kdocs_sheet_index"),
|
||||
("kdocs_unit_column", "kdocs_unit_column"),
|
||||
("kdocs_image_column", "kdocs_image_column"),
|
||||
("kdocs_admin_notify_enabled", "kdocs_admin_notify_enabled"),
|
||||
("kdocs_admin_notify_email", "kdocs_admin_notify_email"),
|
||||
("kdocs_row_start", "kdocs_row_start"),
|
||||
("kdocs_row_end", "kdocs_row_end"),
|
||||
)
|
||||
|
||||
|
||||
def _count_scalar(cursor, sql: str, params=()) -> int:
|
||||
cursor.execute(sql, params)
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
return 0
|
||||
try:
|
||||
if "count" in row.keys():
|
||||
return int(row["count"] or 0)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return int(row[0] or 0)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
|
||||
def _table_exists(cursor, table_name: str) -> bool:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name=?
|
||||
""",
|
||||
(table_name,),
|
||||
)
|
||||
return bool(cursor.fetchone())
|
||||
|
||||
|
||||
def _normalize_days(days, default: int = 30) -> int:
|
||||
try:
|
||||
value = int(days)
|
||||
except Exception:
|
||||
value = default
|
||||
if value < 0:
|
||||
return 0
|
||||
return value
|
||||
|
||||
|
||||
def ensure_default_admin() -> bool:
|
||||
"""确保存在默认管理员账号(行为保持不变)。"""
|
||||
@@ -24,10 +114,9 @@ def ensure_default_admin() -> bool:
|
||||
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT COUNT(*) as count FROM admins")
|
||||
result = cursor.fetchone()
|
||||
count = _count_scalar(cursor, "SELECT COUNT(*) as count FROM admins")
|
||||
|
||||
if result["count"] == 0:
|
||||
if count == 0:
|
||||
alphabet = string.ascii_letters + string.digits
|
||||
random_password = "".join(secrets.choice(alphabet) for _ in range(12))
|
||||
|
||||
@@ -101,41 +190,33 @@ def get_system_stats() -> dict:
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT COUNT(*) as count FROM users")
|
||||
total_users = cursor.fetchone()["count"]
|
||||
|
||||
cursor.execute("SELECT COUNT(*) as count FROM users WHERE status = 'approved'")
|
||||
approved_users = cursor.fetchone()["count"]
|
||||
|
||||
cursor.execute(
|
||||
total_users = _count_scalar(cursor, "SELECT COUNT(*) as count FROM users")
|
||||
approved_users = _count_scalar(cursor, "SELECT COUNT(*) as count FROM users WHERE status = 'approved'")
|
||||
new_users_today = _count_scalar(
|
||||
cursor,
|
||||
"""
|
||||
SELECT COUNT(*) as count
|
||||
FROM users
|
||||
WHERE date(created_at) = date('now', 'localtime')
|
||||
"""
|
||||
""",
|
||||
)
|
||||
new_users_today = cursor.fetchone()["count"]
|
||||
|
||||
cursor.execute(
|
||||
new_users_7d = _count_scalar(
|
||||
cursor,
|
||||
"""
|
||||
SELECT COUNT(*) as count
|
||||
FROM users
|
||||
WHERE datetime(created_at) >= datetime('now', 'localtime', '-7 days')
|
||||
"""
|
||||
""",
|
||||
)
|
||||
new_users_7d = cursor.fetchone()["count"]
|
||||
|
||||
cursor.execute("SELECT COUNT(*) as count FROM accounts")
|
||||
total_accounts = cursor.fetchone()["count"]
|
||||
|
||||
cursor.execute(
|
||||
total_accounts = _count_scalar(cursor, "SELECT COUNT(*) as count FROM accounts")
|
||||
vip_users = _count_scalar(
|
||||
cursor,
|
||||
"""
|
||||
SELECT COUNT(*) as count FROM users
|
||||
WHERE vip_expire_time IS NOT NULL
|
||||
AND datetime(vip_expire_time) > datetime('now', 'localtime')
|
||||
"""
|
||||
""",
|
||||
)
|
||||
vip_users = cursor.fetchone()["count"]
|
||||
|
||||
return {
|
||||
"total_users": total_users,
|
||||
@@ -153,37 +234,9 @@ def get_system_config_raw() -> dict:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM system_config WHERE id = 1")
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row:
|
||||
return dict(row)
|
||||
|
||||
return {
|
||||
"max_concurrent_global": 2,
|
||||
"max_concurrent_per_account": 1,
|
||||
"max_screenshot_concurrent": 3,
|
||||
"schedule_enabled": 0,
|
||||
"schedule_time": "02:00",
|
||||
"schedule_browse_type": "应读",
|
||||
"schedule_weekdays": "1,2,3,4,5,6,7",
|
||||
"proxy_enabled": 0,
|
||||
"proxy_api_url": "",
|
||||
"proxy_expire_minutes": 3,
|
||||
"enable_screenshot": 1,
|
||||
"auto_approve_enabled": 0,
|
||||
"auto_approve_hourly_limit": 10,
|
||||
"auto_approve_vip_days": 7,
|
||||
"kdocs_enabled": 0,
|
||||
"kdocs_doc_url": "",
|
||||
"kdocs_default_unit": "",
|
||||
"kdocs_sheet_name": "",
|
||||
"kdocs_sheet_index": 0,
|
||||
"kdocs_unit_column": "A",
|
||||
"kdocs_image_column": "D",
|
||||
"kdocs_admin_notify_enabled": 0,
|
||||
"kdocs_admin_notify_email": "",
|
||||
"kdocs_row_start": 0,
|
||||
"kdocs_row_end": 0,
|
||||
}
|
||||
return dict(_DEFAULT_SYSTEM_CONFIG)
|
||||
|
||||
|
||||
def update_system_config(
|
||||
@@ -215,127 +268,51 @@ def update_system_config(
|
||||
kdocs_row_end=None,
|
||||
) -> bool:
|
||||
"""更新系统配置(仅更新DB,不做缓存处理)。"""
|
||||
allowed_fields = {
|
||||
"max_concurrent_global",
|
||||
"schedule_enabled",
|
||||
"schedule_time",
|
||||
"schedule_browse_type",
|
||||
"schedule_weekdays",
|
||||
"max_concurrent_per_account",
|
||||
"max_screenshot_concurrent",
|
||||
"enable_screenshot",
|
||||
"proxy_enabled",
|
||||
"proxy_api_url",
|
||||
"proxy_expire_minutes",
|
||||
"auto_approve_enabled",
|
||||
"auto_approve_hourly_limit",
|
||||
"auto_approve_vip_days",
|
||||
"kdocs_enabled",
|
||||
"kdocs_doc_url",
|
||||
"kdocs_default_unit",
|
||||
"kdocs_sheet_name",
|
||||
"kdocs_sheet_index",
|
||||
"kdocs_unit_column",
|
||||
"kdocs_image_column",
|
||||
"kdocs_admin_notify_enabled",
|
||||
"kdocs_admin_notify_email",
|
||||
"kdocs_row_start",
|
||||
"kdocs_row_end",
|
||||
"updated_at",
|
||||
arg_values = {
|
||||
"max_concurrent": max_concurrent,
|
||||
"schedule_enabled": schedule_enabled,
|
||||
"schedule_time": schedule_time,
|
||||
"schedule_browse_type": schedule_browse_type,
|
||||
"schedule_weekdays": schedule_weekdays,
|
||||
"max_concurrent_per_account": max_concurrent_per_account,
|
||||
"max_screenshot_concurrent": max_screenshot_concurrent,
|
||||
"enable_screenshot": enable_screenshot,
|
||||
"proxy_enabled": proxy_enabled,
|
||||
"proxy_api_url": proxy_api_url,
|
||||
"proxy_expire_minutes": proxy_expire_minutes,
|
||||
"auto_approve_enabled": auto_approve_enabled,
|
||||
"auto_approve_hourly_limit": auto_approve_hourly_limit,
|
||||
"auto_approve_vip_days": auto_approve_vip_days,
|
||||
"kdocs_enabled": kdocs_enabled,
|
||||
"kdocs_doc_url": kdocs_doc_url,
|
||||
"kdocs_default_unit": kdocs_default_unit,
|
||||
"kdocs_sheet_name": kdocs_sheet_name,
|
||||
"kdocs_sheet_index": kdocs_sheet_index,
|
||||
"kdocs_unit_column": kdocs_unit_column,
|
||||
"kdocs_image_column": kdocs_image_column,
|
||||
"kdocs_admin_notify_enabled": kdocs_admin_notify_enabled,
|
||||
"kdocs_admin_notify_email": kdocs_admin_notify_email,
|
||||
"kdocs_row_start": kdocs_row_start,
|
||||
"kdocs_row_end": kdocs_row_end,
|
||||
}
|
||||
|
||||
updates = []
|
||||
params = []
|
||||
for db_field, arg_name in _SYSTEM_CONFIG_UPDATERS:
|
||||
value = arg_values.get(arg_name)
|
||||
if value is None:
|
||||
continue
|
||||
updates.append(f"{db_field} = ?")
|
||||
params.append(value)
|
||||
|
||||
if not updates:
|
||||
return False
|
||||
|
||||
updates.append("updated_at = ?")
|
||||
params.append(get_cst_now_str())
|
||||
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
updates = []
|
||||
params = []
|
||||
|
||||
if max_concurrent is not None:
|
||||
updates.append("max_concurrent_global = ?")
|
||||
params.append(max_concurrent)
|
||||
if schedule_enabled is not None:
|
||||
updates.append("schedule_enabled = ?")
|
||||
params.append(schedule_enabled)
|
||||
if schedule_time is not None:
|
||||
updates.append("schedule_time = ?")
|
||||
params.append(schedule_time)
|
||||
if schedule_browse_type is not None:
|
||||
updates.append("schedule_browse_type = ?")
|
||||
params.append(schedule_browse_type)
|
||||
if max_concurrent_per_account is not None:
|
||||
updates.append("max_concurrent_per_account = ?")
|
||||
params.append(max_concurrent_per_account)
|
||||
if max_screenshot_concurrent is not None:
|
||||
updates.append("max_screenshot_concurrent = ?")
|
||||
params.append(max_screenshot_concurrent)
|
||||
if enable_screenshot is not None:
|
||||
updates.append("enable_screenshot = ?")
|
||||
params.append(enable_screenshot)
|
||||
if schedule_weekdays is not None:
|
||||
updates.append("schedule_weekdays = ?")
|
||||
params.append(schedule_weekdays)
|
||||
if proxy_enabled is not None:
|
||||
updates.append("proxy_enabled = ?")
|
||||
params.append(proxy_enabled)
|
||||
if proxy_api_url is not None:
|
||||
updates.append("proxy_api_url = ?")
|
||||
params.append(proxy_api_url)
|
||||
if proxy_expire_minutes is not None:
|
||||
updates.append("proxy_expire_minutes = ?")
|
||||
params.append(proxy_expire_minutes)
|
||||
if auto_approve_enabled is not None:
|
||||
updates.append("auto_approve_enabled = ?")
|
||||
params.append(auto_approve_enabled)
|
||||
if auto_approve_hourly_limit is not None:
|
||||
updates.append("auto_approve_hourly_limit = ?")
|
||||
params.append(auto_approve_hourly_limit)
|
||||
if auto_approve_vip_days is not None:
|
||||
updates.append("auto_approve_vip_days = ?")
|
||||
params.append(auto_approve_vip_days)
|
||||
if kdocs_enabled is not None:
|
||||
updates.append("kdocs_enabled = ?")
|
||||
params.append(kdocs_enabled)
|
||||
if kdocs_doc_url is not None:
|
||||
updates.append("kdocs_doc_url = ?")
|
||||
params.append(kdocs_doc_url)
|
||||
if kdocs_default_unit is not None:
|
||||
updates.append("kdocs_default_unit = ?")
|
||||
params.append(kdocs_default_unit)
|
||||
if kdocs_sheet_name is not None:
|
||||
updates.append("kdocs_sheet_name = ?")
|
||||
params.append(kdocs_sheet_name)
|
||||
if kdocs_sheet_index is not None:
|
||||
updates.append("kdocs_sheet_index = ?")
|
||||
params.append(kdocs_sheet_index)
|
||||
if kdocs_unit_column is not None:
|
||||
updates.append("kdocs_unit_column = ?")
|
||||
params.append(kdocs_unit_column)
|
||||
if kdocs_image_column is not None:
|
||||
updates.append("kdocs_image_column = ?")
|
||||
params.append(kdocs_image_column)
|
||||
if kdocs_admin_notify_enabled is not None:
|
||||
updates.append("kdocs_admin_notify_enabled = ?")
|
||||
params.append(kdocs_admin_notify_enabled)
|
||||
if kdocs_admin_notify_email is not None:
|
||||
updates.append("kdocs_admin_notify_email = ?")
|
||||
params.append(kdocs_admin_notify_email)
|
||||
if kdocs_row_start is not None:
|
||||
updates.append("kdocs_row_start = ?")
|
||||
params.append(kdocs_row_start)
|
||||
if kdocs_row_end is not None:
|
||||
updates.append("kdocs_row_end = ?")
|
||||
params.append(kdocs_row_end)
|
||||
|
||||
if not updates:
|
||||
return False
|
||||
|
||||
updates.append("updated_at = ?")
|
||||
params.append(get_cst_now_str())
|
||||
|
||||
for update_clause in updates:
|
||||
field_name = update_clause.split("=")[0].strip()
|
||||
if field_name not in allowed_fields:
|
||||
raise ValueError(f"非法字段名: {field_name}")
|
||||
|
||||
sql = f"UPDATE system_config SET {', '.join(updates)} WHERE id = 1"
|
||||
cursor.execute(sql, params)
|
||||
conn.commit()
|
||||
@@ -346,13 +323,13 @@ def get_hourly_registration_count() -> int:
|
||||
"""获取最近一小时内的注册用户数"""
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
return _count_scalar(
|
||||
cursor,
|
||||
"""
|
||||
SELECT COUNT(*) FROM users
|
||||
SELECT COUNT(*) as count FROM users
|
||||
WHERE created_at >= datetime('now', 'localtime', '-1 hour')
|
||||
"""
|
||||
""",
|
||||
)
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
|
||||
# ==================== 密码重置(管理员) ====================
|
||||
@@ -374,17 +351,12 @@ def admin_reset_user_password(user_id: int, new_password: str) -> bool:
|
||||
|
||||
def clean_old_operation_logs(days: int = 30) -> int:
|
||||
"""清理指定天数前的操作日志(如果存在operation_logs表)"""
|
||||
safe_days = _normalize_days(days, default=30)
|
||||
|
||||
with db_pool.get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name='operation_logs'
|
||||
"""
|
||||
)
|
||||
|
||||
if not cursor.fetchone():
|
||||
if not _table_exists(cursor, "operation_logs"):
|
||||
return 0
|
||||
|
||||
try:
|
||||
@@ -393,11 +365,11 @@ def clean_old_operation_logs(days: int = 30) -> int:
|
||||
DELETE FROM operation_logs
|
||||
WHERE created_at < datetime('now', 'localtime', '-' || ? || ' days')
|
||||
""",
|
||||
(days,),
|
||||
(safe_days,),
|
||||
)
|
||||
deleted_count = cursor.rowcount
|
||||
conn.commit()
|
||||
print(f"已清理 {deleted_count} 条旧操作日志 (>{days}天)")
|
||||
print(f"已清理 {deleted_count} 条旧操作日志 (>{safe_days}天)")
|
||||
return deleted_count
|
||||
except Exception as e:
|
||||
print(f"清理旧操作日志失败: {e}")
|
||||
|
||||
Reference in New Issue
Block a user