feat: 实现定时任务系统
All checks were successful
continuous-integration/drone/push Build is passing

- 新增 platform_scheduled_tasks, platform_task_logs, platform_script_vars, platform_secrets 数据库表
- 实现 ScriptSDK 提供 AI/通知/DB/HTTP/变量存储/参数获取等功能
- 实现安全的脚本执行器,支持沙箱环境和禁止危险操作
- 实现 APScheduler 调度服务,支持简单时间点和 CRON 表达式
- 新增定时任务 API 路由,包含 CRUD、执行、日志、密钥管理
- 新增定时任务前端页面,支持脚本编辑、测试运行、日志查看
This commit is contained in:
2026-01-28 16:38:19 +08:00
parent 7806072b17
commit 104487f082
19 changed files with 1870 additions and 5406 deletions

View File

@@ -8,6 +8,7 @@ from .stats import AICallEvent, TenantUsageDaily
from .logs import PlatformLog
from .alert import AlertRule, AlertRecord, NotificationChannel
from .pricing import ModelPricing, TenantBilling
from .scheduled_task import ScheduledTask, TaskLog, ScriptVar, Secret
__all__ = [
"Tenant",
@@ -24,5 +25,9 @@ __all__ = [
"AlertRecord",
"NotificationChannel",
"ModelPricing",
"TenantBilling"
"TenantBilling",
"ScheduledTask",
"TaskLog",
"ScriptVar",
"Secret"
]

View File

@@ -18,11 +18,6 @@ class App(Base):
# [{"code": "brainstorm", "name": "头脑风暴", "path": "/brainstorm"}, ...]
tools = Column(Text)
# 配置项定义JSON 数组)- 定义租户可配置的参数
# [{"key": "industry", "label": "行业类型", "type": "radio", "options": [...], "default": "...", "required": false}, ...]
# type: text(文本) | radio(单选) | select(下拉多选) | switch(开关)
config_schema = Column(Text)
# 是否需要企微JS-SDK
require_jssdk = Column(SmallInteger, default=0) # 0-不需要 1-需要

View File

@@ -0,0 +1,96 @@
"""定时任务相关模型"""
from datetime import datetime
from sqlalchemy import Column, BigInteger, Integer, String, Text, Enum, SmallInteger, TIMESTAMP, DateTime
from ..database import Base
class ScheduledTask(Base):
"""定时任务表"""
__tablename__ = "platform_scheduled_tasks"
id = Column(Integer, primary_key=True, autoincrement=True)
tenant_id = Column(String(50))
task_name = Column(String(100), nullable=False)
task_type = Column(Enum('webhook', 'script'), nullable=False, default='script')
# 调度配置
schedule_type = Column(Enum('simple', 'cron'), nullable=False, default='simple')
time_points = Column(Text) # JSON数组 ["08:00", "12:00"]
cron_expression = Column(String(100))
# Webhook配置
webhook_url = Column(String(500))
webhook_method = Column(String(10), default='POST')
webhook_headers = Column(Text) # JSON格式
# 脚本配置
script_content = Column(Text)
script_timeout = Column(Integer, default=300)
# 输入参数
input_params = Column(Text) # JSON格式
# 重试配置
retry_count = Column(Integer, default=0)
retry_interval = Column(Integer, default=60)
# 告警配置
alert_on_failure = Column(SmallInteger, default=0)
alert_webhook = Column(String(500))
# 状态
status = Column(SmallInteger, default=1) # 0-禁用 1-启用
last_run_at = Column(DateTime)
last_run_status = Column(String(20))
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)
class TaskLog(Base):
"""任务执行日志"""
__tablename__ = "platform_task_logs"
id = Column(BigInteger, primary_key=True, autoincrement=True)
task_id = Column(Integer, nullable=False)
tenant_id = Column(String(50))
trace_id = Column(String(100))
status = Column(Enum('running', 'success', 'failed'), nullable=False)
started_at = Column(DateTime, nullable=False)
finished_at = Column(DateTime)
duration_ms = Column(Integer)
output = Column(Text)
error = Column(Text)
retry_count = Column(Integer, default=0)
created_at = Column(TIMESTAMP, default=datetime.now)
class ScriptVar(Base):
"""脚本变量存储"""
__tablename__ = "platform_script_vars"
id = Column(Integer, primary_key=True, autoincrement=True)
task_id = Column(Integer, nullable=False)
tenant_id = Column(String(50))
var_key = Column(String(100), nullable=False)
var_value = Column(Text) # JSON格式
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)
class Secret(Base):
"""密钥管理"""
__tablename__ = "platform_secrets"
id = Column(Integer, primary_key=True, autoincrement=True)
tenant_id = Column(String(50)) # NULL为全局
secret_key = Column(String(100), nullable=False)
secret_value = Column(Text, nullable=False)
description = Column(String(255))
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)

View File

@@ -23,10 +23,6 @@ class TenantApp(Base):
# 功能权限
allowed_tools = Column(Text) # JSON 数组
# 自定义配置JSON 数组)
# [{"key": "industry", "value": "medical_beauty", "remark": "医美行业"}, ...]
custom_configs = Column(Text)
status = Column(SmallInteger, default=1)
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)