- 支持 Python 脚本定时执行(类似青龙面板) - 内置 SDK:AI大模型、钉钉/企微通知、数据库查询、HTTP请求、变量存储 - 安全沙箱执行,禁用危险模块 - 前端脚本编辑器,支持测试执行 - SDK 文档查看 - 日志通过 TraceID 与 platform_logs 关联
This commit is contained in:
@@ -28,8 +28,10 @@ class TaskCreate(BaseModel):
|
||||
schedule_type: str = "simple" # simple | cron
|
||||
time_points: Optional[List[str]] = None # ["09:00", "14:00"]
|
||||
cron_expression: Optional[str] = None # "0 9,14 * * *"
|
||||
webhook_url: str
|
||||
execution_type: str = "webhook" # webhook | script
|
||||
webhook_url: Optional[str] = None
|
||||
input_params: Optional[dict] = None
|
||||
script_content: Optional[str] = None
|
||||
is_enabled: bool = True
|
||||
|
||||
|
||||
@@ -39,8 +41,15 @@ class TaskUpdate(BaseModel):
|
||||
schedule_type: Optional[str] = None
|
||||
time_points: Optional[List[str]] = None
|
||||
cron_expression: Optional[str] = None
|
||||
execution_type: Optional[str] = None
|
||||
webhook_url: Optional[str] = None
|
||||
input_params: Optional[dict] = None
|
||||
script_content: Optional[str] = None
|
||||
|
||||
|
||||
class ScriptTestRequest(BaseModel):
|
||||
tenant_id: str
|
||||
script_content: str
|
||||
|
||||
|
||||
# API Endpoints
|
||||
@@ -110,6 +119,14 @@ async def create_task(
|
||||
if not data.cron_expression:
|
||||
raise HTTPException(status_code=400, detail="CRON模式需要提供表达式")
|
||||
|
||||
# 验证执行配置
|
||||
if data.execution_type == "webhook":
|
||||
if not data.webhook_url:
|
||||
raise HTTPException(status_code=400, detail="Webhook模式需要提供URL")
|
||||
elif data.execution_type == "script":
|
||||
if not data.script_content:
|
||||
raise HTTPException(status_code=400, detail="脚本模式需要提供脚本内容")
|
||||
|
||||
# 插入数据库
|
||||
import json
|
||||
time_points_json = json.dumps(data.time_points) if data.time_points else None
|
||||
@@ -119,9 +136,9 @@ async def create_task(
|
||||
text("""
|
||||
INSERT INTO platform_scheduled_tasks
|
||||
(tenant_id, task_name, task_desc, schedule_type, time_points,
|
||||
cron_expression, webhook_url, input_params, is_enabled)
|
||||
cron_expression, execution_type, webhook_url, input_params, script_content, is_enabled)
|
||||
VALUES (:tenant_id, :task_name, :task_desc, :schedule_type, :time_points,
|
||||
:cron_expression, :webhook_url, :input_params, :is_enabled)
|
||||
:cron_expression, :execution_type, :webhook_url, :input_params, :script_content, :is_enabled)
|
||||
"""),
|
||||
{
|
||||
"tenant_id": data.tenant_id,
|
||||
@@ -130,8 +147,10 @@ async def create_task(
|
||||
"schedule_type": data.schedule_type,
|
||||
"time_points": time_points_json,
|
||||
"cron_expression": data.cron_expression,
|
||||
"execution_type": data.execution_type,
|
||||
"webhook_url": data.webhook_url,
|
||||
"input_params": input_params_json,
|
||||
"script_content": data.script_content,
|
||||
"is_enabled": 1 if data.is_enabled else 0
|
||||
}
|
||||
)
|
||||
@@ -209,9 +228,15 @@ async def update_task(
|
||||
if data.cron_expression is not None:
|
||||
updates.append("cron_expression = :cron_expression")
|
||||
params["cron_expression"] = data.cron_expression
|
||||
if data.execution_type is not None:
|
||||
updates.append("execution_type = :execution_type")
|
||||
params["execution_type"] = data.execution_type
|
||||
if data.webhook_url is not None:
|
||||
updates.append("webhook_url = :webhook_url")
|
||||
params["webhook_url"] = data.webhook_url
|
||||
if data.script_content is not None:
|
||||
updates.append("script_content = :script_content")
|
||||
params["script_content"] = data.script_content
|
||||
if data.input_params is not None:
|
||||
updates.append("input_params = :input_params")
|
||||
params["input_params"] = json.dumps(data.input_params)
|
||||
@@ -354,3 +379,96 @@ async def get_task_logs(
|
||||
"size": size,
|
||||
"items": logs
|
||||
}
|
||||
|
||||
|
||||
@router.post("/test-script")
|
||||
async def test_script(
|
||||
data: ScriptTestRequest,
|
||||
user: User = Depends(require_operator)
|
||||
):
|
||||
"""测试执行脚本(不记录日志)"""
|
||||
from ..services.script_executor import test_script as run_test
|
||||
|
||||
if not data.script_content or not data.script_content.strip():
|
||||
raise HTTPException(status_code=400, detail="脚本内容不能为空")
|
||||
|
||||
result = await run_test(
|
||||
tenant_id=data.tenant_id,
|
||||
script_content=data.script_content
|
||||
)
|
||||
|
||||
return result.to_dict()
|
||||
|
||||
|
||||
@router.get("/sdk-docs")
|
||||
async def get_sdk_docs():
|
||||
"""获取 SDK 文档"""
|
||||
return {
|
||||
"description": "脚本执行 SDK 文档",
|
||||
"methods": [
|
||||
{
|
||||
"name": "ai(prompt, system=None, model='gemini-2.5-flash')",
|
||||
"description": "调用大模型生成内容",
|
||||
"example": "result = ai('帮我写一段营销文案')"
|
||||
},
|
||||
{
|
||||
"name": "dingtalk(webhook, content, msg_type='text', at_mobiles=None)",
|
||||
"description": "发送钉钉群消息",
|
||||
"example": "dingtalk('https://oapi.dingtalk.com/robot/send?access_token=xxx', '消息内容')"
|
||||
},
|
||||
{
|
||||
"name": "wecom(webhook, content, msg_type='text')",
|
||||
"description": "发送企业微信群消息",
|
||||
"example": "wecom('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx', '消息内容')"
|
||||
},
|
||||
{
|
||||
"name": "db(sql, params=None)",
|
||||
"description": "执行 SQL 查询(仅支持 SELECT)",
|
||||
"example": "rows = db('SELECT * FROM users WHERE id = :id', {'id': 1})"
|
||||
},
|
||||
{
|
||||
"name": "http_get(url, headers=None, params=None)",
|
||||
"description": "发送 HTTP GET 请求",
|
||||
"example": "data = http_get('https://api.example.com/data')"
|
||||
},
|
||||
{
|
||||
"name": "http_post(url, data=None, json_data=None, headers=None)",
|
||||
"description": "发送 HTTP POST 请求",
|
||||
"example": "data = http_post('https://api.example.com/submit', json_data={'key': 'value'})"
|
||||
},
|
||||
{
|
||||
"name": "get_var(key, default=None)",
|
||||
"description": "获取存储的变量(跨执行持久化)",
|
||||
"example": "count = get_var('run_count', 0)"
|
||||
},
|
||||
{
|
||||
"name": "set_var(key, value)",
|
||||
"description": "存储变量(跨执行持久化)",
|
||||
"example": "set_var('run_count', count + 1)"
|
||||
},
|
||||
{
|
||||
"name": "log(message, level='INFO')",
|
||||
"description": "记录日志",
|
||||
"example": "log('任务执行完成')"
|
||||
}
|
||||
],
|
||||
"example_script": '''# 示例:每日推送 AI 生成的内容到钉钉
|
||||
import json
|
||||
|
||||
# 获取历史数据
|
||||
history = get_var('history', [])
|
||||
|
||||
# 调用 AI 生成内容
|
||||
prompt = f"根据以下信息生成今日营销文案:{json.dumps(history[-5:], ensure_ascii=False)}"
|
||||
content = ai(prompt, system="你是一个专业的营销文案专家")
|
||||
|
||||
# 发送到钉钉
|
||||
dingtalk(
|
||||
webhook="你的钉钉机器人Webhook",
|
||||
content=content
|
||||
)
|
||||
|
||||
# 记录日志
|
||||
log(f"已发送: {content[:50]}...")
|
||||
'''
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user