新增定时调度任务
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"""SQLite数据库操作模块 - 使用标准库sqlite3"""
|
||||
import sqlite3
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
from ..Convention.Runtime.Config import *
|
||||
from ..Convention.Runtime.GlobalConfig import ProjectConfig, ConsoleFrontColor
|
||||
from ..Convention.Runtime.Architecture import Architecture
|
||||
@@ -9,6 +11,12 @@ logger: ProjectConfig = Architecture.Get(ProjectConfig)
|
||||
DATABASE_PATH = logger.GetFile(logger.FindItem("database_path", "db.db"), False).GetFullPath()
|
||||
logger.SaveProperties()
|
||||
|
||||
SCHEDULED_TASK_TABLE = "scheduled_tasks"
|
||||
STATUS_PENDING = "pending"
|
||||
STATUS_RUNNING = "running"
|
||||
STATUS_COMPLETED = "completed"
|
||||
STATUS_FAILED = "failed"
|
||||
|
||||
class Database:
|
||||
"""数据库管理类"""
|
||||
|
||||
@@ -123,7 +131,112 @@ class Database:
|
||||
|
||||
def init_tables(self):
|
||||
"""初始化数据库表"""
|
||||
pass
|
||||
self._ensure_scheduled_tasks_table()
|
||||
|
||||
def _ensure_scheduled_tasks_table(self) -> None:
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute(
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS {SCHEDULED_TASK_TABLE} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
plugin_module TEXT NOT NULL,
|
||||
plugin_class TEXT,
|
||||
callback_name TEXT NOT NULL,
|
||||
payload TEXT,
|
||||
execute_at INTEGER NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT '{STATUS_PENDING}',
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
last_error TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
cursor.execute(
|
||||
f"CREATE INDEX IF NOT EXISTS idx_{SCHEDULED_TASK_TABLE}_status_execute_at ON {SCHEDULED_TASK_TABLE}(status, execute_at)"
|
||||
)
|
||||
|
||||
def create_scheduled_task(
|
||||
self,
|
||||
plugin_module: str,
|
||||
plugin_class: Optional[str],
|
||||
callback_name: str,
|
||||
payload: Optional[str],
|
||||
execute_at_ms: int
|
||||
) -> int:
|
||||
now_ms = int(time.time() * 1000)
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute(
|
||||
f"""
|
||||
INSERT INTO {SCHEDULED_TASK_TABLE} (
|
||||
plugin_module, plugin_class, callback_name, payload,
|
||||
execute_at, status, attempts, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
plugin_module,
|
||||
plugin_class,
|
||||
callback_name,
|
||||
payload,
|
||||
execute_at_ms,
|
||||
STATUS_PENDING,
|
||||
0,
|
||||
now_ms,
|
||||
now_ms,
|
||||
)
|
||||
)
|
||||
task_id = cursor.lastrowid
|
||||
return int(task_id)
|
||||
|
||||
def get_due_tasks(self, now_ms: int, limit: int) -> list[sqlite3.Row]:
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute(
|
||||
f"""
|
||||
SELECT * FROM {SCHEDULED_TASK_TABLE}
|
||||
WHERE status = ? AND execute_at <= ?
|
||||
ORDER BY execute_at ASC
|
||||
LIMIT ?
|
||||
""",
|
||||
(STATUS_PENDING, now_ms, limit)
|
||||
)
|
||||
return cursor.fetchall()
|
||||
|
||||
def update_task_status(
|
||||
self,
|
||||
task_id: int,
|
||||
status: str,
|
||||
*,
|
||||
attempts: Optional[int] = None,
|
||||
last_error: Optional[str] = None
|
||||
) -> None:
|
||||
now_ms = int(time.time() * 1000)
|
||||
sets = ["status = ?"]
|
||||
params: list[Any] = [status]
|
||||
if attempts is not None:
|
||||
sets.append("attempts = ?")
|
||||
params.append(attempts)
|
||||
if last_error is not None:
|
||||
sets.append("last_error = ?")
|
||||
params.append(last_error)
|
||||
params.append(task_id)
|
||||
set_clause = ", ".join(sets)
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute(
|
||||
f"UPDATE {SCHEDULED_TASK_TABLE} SET {set_clause} WHERE id = ?",
|
||||
params
|
||||
)
|
||||
|
||||
def reset_running_tasks(self) -> None:
|
||||
cursor = self.conn.cursor()
|
||||
now_ms = int(time.time() * 1000)
|
||||
cursor.execute(
|
||||
f"""
|
||||
UPDATE {SCHEDULED_TASK_TABLE}
|
||||
SET status = ?, updated_at = ?
|
||||
WHERE status = ?
|
||||
""",
|
||||
(STATUS_PENDING, now_ms, STATUS_RUNNING)
|
||||
)
|
||||
|
||||
def close(self):
|
||||
"""关闭数据库连接"""
|
||||
@@ -132,10 +245,19 @@ class Database:
|
||||
self._conn = None
|
||||
logger.Log("Info", f"{ConsoleFrontColor.GREEN}数据库连接已关闭{ConsoleFrontColor.RESET}")
|
||||
|
||||
|
||||
def get_db() -> Database:
|
||||
"""获取全局数据库实例(单例模式)"""
|
||||
if not Architecture.Contains(Database):
|
||||
return Database()
|
||||
return Architecture.Get(Database)
|
||||
|
||||
__all__ = ["get_db"]
|
||||
__all__ = [
|
||||
"get_db",
|
||||
"Database",
|
||||
"SCHEDULED_TASK_TABLE",
|
||||
"STATUS_PENDING",
|
||||
"STATUS_RUNNING",
|
||||
"STATUS_COMPLETED",
|
||||
"STATUS_FAILED",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user