feat: 实现通知渠道管理功能
All checks were successful
continuous-integration/drone/push Build is passing

- 新增 platform_notification_channels 表管理通知渠道(钉钉/企微机器人)
- 新增通知渠道管理页面,支持创建、编辑、测试、删除
- 定时任务增加通知渠道选择和企微应用选择
- 脚本执行支持返回值(result变量),自动发送到配置的渠道
- 调度器执行脚本后根据配置自动发送通知

使用方式:
1. 在「通知渠道」页面为租户配置钉钉/企微机器人
2. 创建定时任务时选择通知渠道
3. 脚本中设置 result = {'content': '内容', 'title': '标题'}
4. 任务执行后自动发送到配置的渠道
This commit is contained in:
2026-01-28 17:02:20 +08:00
parent d9fa9708ce
commit 2fbba63884
12 changed files with 800 additions and 25 deletions

View File

@@ -49,7 +49,7 @@ class ScriptExecutor:
trace_id: Optional[str] = None,
params: Optional[Dict[str, Any]] = None,
timeout: int = 300
) -> Tuple[bool, str, str]:
) -> Tuple[bool, str, str, Optional[Dict]]:
"""执行脚本
Args:
@@ -61,7 +61,8 @@ class ScriptExecutor:
timeout: 超时秒数
Returns:
(success, output, error)
(success, output, error, result)
result: 脚本返回值 {'content': '...', 'title': '...'}
"""
# 创建SDK实例
sdk = ScriptSDK(
@@ -75,7 +76,7 @@ class ScriptExecutor:
# 检查脚本安全性
check_result = self._check_script_safety(script_content)
if check_result:
return False, '', f"脚本安全检查失败: {check_result}"
return False, '', f"脚本安全检查失败: {check_result}", None
# 准备执行环境
safe_globals = self._create_safe_globals(sdk)
@@ -101,11 +102,22 @@ class ScriptExecutor:
# 合并输出
output = '\n'.join(filter(None, [sdk_output, stdout_output]))
return True, output, ''
# 获取脚本返回值(通过 __result__ 变量)
result = safe_globals.get('__result__')
if result is None and 'result' in safe_globals:
result = safe_globals.get('result')
# 如果返回的是字符串,包装成字典
if isinstance(result, str):
result = {'content': result}
elif result is not None and not isinstance(result, dict):
result = {'content': str(result)}
return True, output, '', result
except Exception as e:
error_msg = f"{type(e).__name__}: {str(e)}\n{traceback.format_exc()}"
return False, sdk.get_output(), error_msg
return False, sdk.get_output(), error_msg, None
finally:
sys.stdout = old_stdout
@@ -223,12 +235,12 @@ class ScriptExecutor:
"output": str,
"error": str,
"duration_ms": int,
"logs": [...]
"result": dict
}
"""
start_time = datetime.now()
success, output, error = self.execute(
success, output, error, result = self.execute(
script_content=script_content,
task_id=task_id,
tenant_id=tenant_id,
@@ -242,5 +254,6 @@ class ScriptExecutor:
"success": success,
"output": output,
"error": error,
"duration_ms": duration_ms
"duration_ms": duration_ms,
"result": result
}