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