mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-11 01:36:37 +08:00
修复任务参数类型转换
This commit is contained in:
@@ -6,6 +6,7 @@ import {Button} from "@/components/ui/button"
|
|||||||
import {Input} from "@/components/ui/input"
|
import {Input} from "@/components/ui/input"
|
||||||
import {Label} from "@/components/ui/label"
|
import {Label} from "@/components/ui/label"
|
||||||
import {Textarea} from "@/components/ui/textarea"
|
import {Textarea} from "@/components/ui/textarea"
|
||||||
|
import {Switch} from "@/components/ui/switch"
|
||||||
import {Spinner} from "@/components/ui/spinner"
|
import {Spinner} from "@/components/ui/spinner"
|
||||||
import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle} from "@/components/ui/dialog"
|
import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle} from "@/components/ui/dialog"
|
||||||
import {Calendar as CalendarIcon, Clock, Info, Layers, Play} from "lucide-react"
|
import {Calendar as CalendarIcon, Clock, Info, Layers, Play} from "lucide-react"
|
||||||
@@ -380,6 +381,17 @@ export function TaskManager() {
|
|||||||
value={paramValues[param.name] || ""}
|
value={paramValues[param.name] || ""}
|
||||||
onChange={(e) => setParamValues(prev => ({ ...prev, [param.name]: e.target.value }))}
|
onChange={(e) => setParamValues(prev => ({ ...prev, [param.name]: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
|
) : param.type === 'boolean' ? (
|
||||||
|
<div className="flex items-center gap-2 pt-1 h-9">
|
||||||
|
<Switch
|
||||||
|
id={`param-${param.name}`}
|
||||||
|
checked={paramValues[param.name] === 'true'}
|
||||||
|
onCheckedChange={(checked) => setParamValues(prev => ({ ...prev, [param.name]: checked ? 'true' : 'false' }))}
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{paramValues[param.name] === 'true' ? '开启' : '关闭'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
id={`param-${param.name}`}
|
id={`param-${param.name}`}
|
||||||
|
|||||||
@@ -404,6 +404,17 @@ export function TaskSchedulesManager() {
|
|||||||
value={paramValues[param.name] || ""}
|
value={paramValues[param.name] || ""}
|
||||||
onChange={(e) => setParamValues(prev => ({ ...prev, [param.name]: e.target.value }))}
|
onChange={(e) => setParamValues(prev => ({ ...prev, [param.name]: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
|
) : param.type === 'boolean' ? (
|
||||||
|
<div className="flex items-center gap-2 pt-1 h-9">
|
||||||
|
<Switch
|
||||||
|
id={`param-${param.name}`}
|
||||||
|
checked={paramValues[param.name] === 'true'}
|
||||||
|
onCheckedChange={(checked) => setParamValues(prev => ({ ...prev, [param.name]: checked ? 'true' : 'false' }))}
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{paramValues[param.name] === 'true' ? '开启' : '关闭'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
id={`param-${param.name}`}
|
id={`param-${param.name}`}
|
||||||
|
|||||||
@@ -97,8 +97,9 @@ export interface TaskTypeResponse {
|
|||||||
* - string → 单行文本输入,JSON 中序列化为 string
|
* - string → 单行文本输入,JSON 中序列化为 string
|
||||||
* - text → 多行文本输入,JSON 中序列化为 string
|
* - text → 多行文本输入,JSON 中序列化为 string
|
||||||
* - number → 数字输入,JSON 中序列化为 number(而非 string)
|
* - number → 数字输入,JSON 中序列化为 number(而非 string)
|
||||||
|
* - boolean → 开关输入,JSON 中序列化为 boolean(而非 string)
|
||||||
*/
|
*/
|
||||||
export type TaskParamType = 'string' | 'text' | 'number';
|
export type TaskParamType = 'string' | 'text' | 'number' | 'boolean';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务参数定义
|
* 任务参数定义
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export interface BuildPayloadResult {
|
|||||||
function coerceParamValue(
|
function coerceParamValue(
|
||||||
param: TaskParam,
|
param: TaskParam,
|
||||||
rawValue: string,
|
rawValue: string,
|
||||||
): { value: string | number; ok: true } | { value: undefined; ok: true } | { error: string; ok: false } {
|
): { value: string | number | boolean; ok: true } | { value: undefined; ok: true } | { error: string; ok: false } {
|
||||||
const trimmed = rawValue.trim();
|
const trimmed = rawValue.trim();
|
||||||
|
|
||||||
switch (param.type) {
|
switch (param.type) {
|
||||||
@@ -47,6 +47,19 @@ function coerceParamValue(
|
|||||||
}
|
}
|
||||||
return { value: num, ok: true };
|
return { value: num, ok: true };
|
||||||
}
|
}
|
||||||
|
case 'boolean': {
|
||||||
|
if (trimmed === '') {
|
||||||
|
// 空值 → 省略该字段
|
||||||
|
return { value: undefined, ok: true };
|
||||||
|
}
|
||||||
|
const val = trimmed.toLowerCase();
|
||||||
|
if (val === 'true') {
|
||||||
|
return { value: true, ok: true };
|
||||||
|
} else if (val === 'false') {
|
||||||
|
return { value: false, ok: true };
|
||||||
|
}
|
||||||
|
return { error: `「${param.label}」必须是 true 或 false`, ok: false };
|
||||||
|
}
|
||||||
case 'string':
|
case 'string':
|
||||||
case 'text':
|
case 'text':
|
||||||
default:
|
default:
|
||||||
@@ -62,6 +75,7 @@ function coerceParamValue(
|
|||||||
* - `number` 类型参数:序列化为 JSON number,而非 JSON string。
|
* - `number` 类型参数:序列化为 JSON number,而非 JSON string。
|
||||||
* - `number` 类型参数为空且非必填:从 payload 中省略该字段(不发送),
|
* - `number` 类型参数为空且非必填:从 payload 中省略该字段(不发送),
|
||||||
* 避免后端收到 `"target_type":""` 再尝试解析为 int 时报错。
|
* 避免后端收到 `"target_type":""` 再尝试解析为 int 时报错。
|
||||||
|
* - `boolean` 类型参数:序列化为 JSON boolean。
|
||||||
* - `required` 字段为空:返回 error。
|
* - `required` 字段为空:返回 error。
|
||||||
*
|
*
|
||||||
* @param params 任务的参数定义列表(来自 TaskMeta.params)
|
* @param params 任务的参数定义列表(来自 TaskMeta.params)
|
||||||
@@ -72,7 +86,7 @@ export function buildTaskPayload(
|
|||||||
params: TaskParam[],
|
params: TaskParam[],
|
||||||
values: Record<string, string>,
|
values: Record<string, string>,
|
||||||
): BuildPayloadResult {
|
): BuildPayloadResult {
|
||||||
const payloadData: Record<string, string | number> = {};
|
const payloadData: Record<string, string | number | boolean> = {};
|
||||||
|
|
||||||
for (const param of params) {
|
for (const param of params) {
|
||||||
const raw = values[param.name] ?? '';
|
const raw = values[param.name] ?? '';
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func RetryTask(c *gin.Context) {
|
|||||||
switch {
|
switch {
|
||||||
case strings.Contains(errMsg, "不存在"):
|
case strings.Contains(errMsg, "不存在"):
|
||||||
c.JSON(http.StatusNotFound, util.Err(errMsg))
|
c.JSON(http.StatusNotFound, util.Err(errMsg))
|
||||||
case strings.Contains(errMsg, "只有非进行中") || strings.Contains(errMsg, "不支持重试") || strings.Contains(errMsg, "已达到最大重试"):
|
case strings.Contains(errMsg, "只有失败的任务") || strings.Contains(errMsg, "不支持重试") || strings.Contains(errMsg, "已达到最大重试"):
|
||||||
c.JSON(http.StatusBadRequest, util.Err(errMsg))
|
c.JSON(http.StatusBadRequest, util.Err(errMsg))
|
||||||
default:
|
default:
|
||||||
c.JSON(http.StatusInternalServerError, util.Err(fmt.Sprintf("%s: %v", TaskRetryFailed, err)))
|
c.JSON(http.StatusInternalServerError, util.Err(fmt.Sprintf("%s: %v", TaskRetryFailed, err)))
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const defaultMaxRetry = 3
|
|||||||
type TaskParam struct {
|
type TaskParam struct {
|
||||||
Name string `json:"Name"` // 参数键名
|
Name string `json:"Name"` // 参数键名
|
||||||
Label string `json:"Label"` // 显示名称
|
Label string `json:"Label"` // 显示名称
|
||||||
Type string `json:"Type"` // 类型:string, text, number
|
Type string `json:"Type"` // 类型:string, text, number, boolean
|
||||||
Required bool `json:"Required"` // 是否必填
|
Required bool `json:"Required"` // 是否必填
|
||||||
Placeholder string `json:"Placeholder"` // 占位符
|
Placeholder string `json:"Placeholder"` // 占位符
|
||||||
Description string `json:"Description"` // 描述
|
Description string `json:"Description"` // 描述
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const (
|
|||||||
errCreateTaskExecutionFailed = "创建任务执行记录失败: %w"
|
errCreateTaskExecutionFailed = "创建任务执行记录失败: %w"
|
||||||
errTaskEnqueueFailed = "任务入队失败: %w"
|
errTaskEnqueueFailed = "任务入队失败: %w"
|
||||||
errTaskExecutionNotFound = "任务执行记录不存在: %w"
|
errTaskExecutionNotFound = "任务执行记录不存在: %w"
|
||||||
errRetryOnlyFinishedTask = "只有非进行中的任务才能重试,当前状态: %s"
|
errRetryOnlyFailedTask = "只有失败的任务才能重试,当前状态: %s"
|
||||||
errTaskNotRetryable = "该任务不支持重试"
|
errTaskNotRetryable = "该任务不支持重试"
|
||||||
errTaskMaxRetryExceeded = "已达到最大重试次数 %d"
|
errTaskMaxRetryExceeded = "已达到最大重试次数 %d"
|
||||||
errCreateRetryExecutionFailed = "创建重试任务执行记录失败: %w"
|
errCreateRetryExecutionFailed = "创建重试任务执行记录失败: %w"
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ func RetryTask(ctx context.Context, id uint64) (string, error) {
|
|||||||
return "", fmt.Errorf(errTaskExecutionNotFound, err)
|
return "", fmt.Errorf(errTaskExecutionNotFound, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if execution.Status == model.TaskExecutionStatusPending || execution.Status == model.TaskExecutionStatusRunning {
|
if execution.Status != model.TaskExecutionStatusFailed {
|
||||||
return "", fmt.Errorf(errRetryOnlyFinishedTask, execution.Status)
|
return "", fmt.Errorf(errRetryOnlyFailedTask, execution.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !execution.Retryable {
|
if !execution.Retryable {
|
||||||
|
|||||||
Reference in New Issue
Block a user