diff --git a/src/modules/scripting/Application/Services/IScriptExecutionService.cs b/src/modules/scripting/Application/Services/IScriptExecutionService.cs
index 0cc9eb7..c042291 100644
--- a/src/modules/scripting/Application/Services/IScriptExecutionService.cs
+++ b/src/modules/scripting/Application/Services/IScriptExecutionService.cs
@@ -2,7 +2,7 @@ using Scripting.Application.DTOs;
namespace Scripting.Application.Services;
-/// 脚本执行服务:编译+运行 C# 脚本,注入 MediatR/CurrentUser/CancellationToken,并写执行日志。
+/// 脚本执行服务:编译+运行 C# 脚本,注入 MediatR/CurrentUser/CancellationToken/Logger,并写执行日志。
public interface IScriptExecutionService
{
/// 按 Id 执行已发布脚本(按钮动作链用)。
@@ -12,5 +12,5 @@ public interface IScriptExecutionService
Task ExecuteByNameAsync(string scriptName, string inputJson, CancellationToken ct);
/// 测试运行:不校验 Published 状态,允许草稿直接测,仍写日志(FunctionName 标 [TEST])。
- Task TestExecuteAsync(Guid scriptId, string inputJson, CancellationToken ct);
+ Task ExecuteTestAsync(Guid scriptId, string inputJson, CancellationToken ct);
}
diff --git a/src/modules/scripting/Application/Services/ScriptExecutionService.cs b/src/modules/scripting/Application/Services/ScriptExecutionService.cs
index 8eac9dc..0f8b603 100644
--- a/src/modules/scripting/Application/Services/ScriptExecutionService.cs
+++ b/src/modules/scripting/Application/Services/ScriptExecutionService.cs
@@ -57,6 +57,12 @@ public partial class ScriptExecutionService(
RegexOptions.Compiled | RegexOptions.CultureInvariant)]
private static partial Regex ForbiddenApiRegex { get; }
+ /// 严格 ISO-8601 日期正则:要求 YYYY-MM-DD(可带时间/时区)。防止 "2026"/"2026-07" 等日期样字符串误判为 DateTime。
+ [GeneratedRegex(
+ @"^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$",
+ RegexOptions.Compiled | RegexOptions.CultureInvariant)]
+ private static partial Regex Iso8601Regex { get; }
+
// ── 公共入口 ──────────────────────────────────────────────────────────
///
@@ -68,7 +74,7 @@ public partial class ScriptExecutionService(
=> ExecuteAsync(null, scriptName, inputJson, requirePublished: true, isTest: false, ct);
///
- public Task TestExecuteAsync(Guid scriptId, string inputJson, CancellationToken ct)
+ public Task ExecuteTestAsync(Guid scriptId, string inputJson, CancellationToken ct)
=> ExecuteAsync(scriptId, null, inputJson, requirePublished: false, isTest: true, ct);
// ── 核心执行 ──────────────────────────────────────────────────────────
@@ -117,12 +123,13 @@ public partial class ScriptExecutionService(
throw new BusinessException($"脚本包含禁用 API: {forbiddenMatch.Value}");
}
- // 深度计数 +1(finally 归位)
- _recursionDepth.Value++;
var stopwatch = Stopwatch.StartNew();
var logger = new ScriptLogger();
try
{
+ // 深度计数 +1(finally 归位),置于 try 内保证与 finally 配对
+ _recursionDepth.Value++;
+
// 超时令牌:默认 5s,上限 30s
var timeoutMs = function.TimeoutMs <= 0 ? DefaultTimeoutMs : Math.Min(function.TimeoutMs, MaxTimeoutMs);
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
@@ -170,6 +177,17 @@ public partial class ScriptExecutionService(
inputJson, null, ex.Message, stopwatch.ElapsedMilliseconds, logger.ToText(), ct);
throw new BusinessException($"脚本执行失败: {ex.Message}");
}
+ catch (Exception ex)
+ {
+ // 脚本运行时异常(NullReference/DivideByZero/IndexOutOfRange/InvalidCast/KeyNotFound 等):
+ // 统一记录 Failed 日志并包装为 BusinessException,避免原始异常冒泡成 500 带堆栈。
+ // 超时场景下 ct 可能已取消,用 CancellationToken.None 保证日志落库
+ stopwatch.Stop();
+ await AddLogAsync(function, isTest, ScriptExecutionStatus.Failed,
+ inputJson, null, ex.Message, stopwatch.ElapsedMilliseconds, logger.ToText(),
+ ct.IsCancellationRequested ? CancellationToken.None : ct);
+ throw new BusinessException($"脚本执行失败: {ex.Message}");
+ }
finally
{
_recursionDepth.Value--;
@@ -301,16 +319,17 @@ public partial class ScriptExecutionService(
{
var str = element.GetString() ?? string.Empty;
- // ISO 8601 日期优先
- if (DateTime.TryParse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dt))
- {
- return dt;
- }
- // Guid
+ // Guid 优先(Guid 串不会匹配 ISO-8601 日期正则)
if (Guid.TryParse(str, out var guid))
{
return guid;
}
+ // 严格 ISO-8601 日期:先正则白名单,再 TryParse,避免 "2026"/"2026-07" 等日期样字符串误判
+ if (Iso8601Regex.IsMatch(str)
+ && DateTime.TryParse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dt))
+ {
+ return dt;
+ }
return str;
}