feat: discovery tasks 接口支持多状态查询
- 新增 `statuses` 查询参数,支持按多个状态过滤任务 - 保持 `status` 单状态参数的向后兼容性 - 更新验证 schema 以支持逗号分隔的多状态值 示例用法: - 单状态:?status=PENDING - 多状态:?statuses=PENDING,FAILED
This commit is contained in:
@@ -105,6 +105,7 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
const validation = GetDiscoveryTasksQuerySchema.safeParse({
|
const validation = GetDiscoveryTasksQuerySchema.safeParse({
|
||||||
status: searchParams.get('status') || undefined,
|
status: searchParams.get('status') || undefined,
|
||||||
|
statuses: searchParams.get('statuses') || undefined, // 新增:支持多状态查询
|
||||||
limit: searchParams.get('limit') || '10',
|
limit: searchParams.get('limit') || '10',
|
||||||
offset: searchParams.get('offset') || '0',
|
offset: searchParams.get('offset') || '0',
|
||||||
})
|
})
|
||||||
@@ -120,17 +121,27 @@ export async function GET(request: NextRequest) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status, limit, offset } = validation.data
|
const { status, statuses, limit, offset } = validation.data
|
||||||
|
|
||||||
|
// 构建查询条件:支持单状态和多状态查询
|
||||||
|
let whereClause = {}
|
||||||
|
if (statuses && statuses.length > 0) {
|
||||||
|
// 多状态查询:?statuses=PENDING,FAILED
|
||||||
|
whereClause = { status: { in: statuses } }
|
||||||
|
} else if (status) {
|
||||||
|
// 单状态查询(向后兼容):?status=PENDING
|
||||||
|
whereClause = { status }
|
||||||
|
}
|
||||||
|
|
||||||
const tasks = await prisma.projectDiscoveryTask.findMany({
|
const tasks = await prisma.projectDiscoveryTask.findMany({
|
||||||
where: status ? { status } : undefined,
|
where: whereClause,
|
||||||
orderBy: { createdAt: 'asc' },
|
orderBy: { createdAt: 'asc' },
|
||||||
take: limit,
|
take: limit,
|
||||||
skip: offset,
|
skip: offset,
|
||||||
})
|
})
|
||||||
|
|
||||||
const total = await prisma.projectDiscoveryTask.count({
|
const total = await prisma.projectDiscoveryTask.count({
|
||||||
where: status ? { status } : undefined,
|
where: whereClause,
|
||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
|
|||||||
+148
-1
@@ -104,7 +104,12 @@ export const UpdateDiscoveryTaskSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const GetDiscoveryTasksQuerySchema = z.object({
|
export const GetDiscoveryTasksQuerySchema = z.object({
|
||||||
status: TaskStatusEnum.optional(),
|
status: TaskStatusEnum.optional(), // 保持向后兼容:单状态查询
|
||||||
|
statuses: z.string().optional().transform((val) => {
|
||||||
|
// 将逗号分隔的字符串转换为数组,如 "PENDING,FAILED" → ["PENDING", "FAILED"]
|
||||||
|
if (!val) return undefined;
|
||||||
|
return val.split(',').map(s => s.trim() as TaskStatus).filter(Boolean);
|
||||||
|
}),
|
||||||
limit: z.coerce.number().int().positive().max(100).default(10),
|
limit: z.coerce.number().int().positive().max(100).default(10),
|
||||||
offset: z.coerce.number().int().nonnegative().default(0),
|
offset: z.coerce.number().int().nonnegative().default(0),
|
||||||
});
|
});
|
||||||
@@ -159,6 +164,134 @@ export const ProjectQuerySchema = z.object({
|
|||||||
limit: z.coerce.number().int().positive().max(100).default(20),
|
limit: z.coerce.number().int().positive().max(100).default(20),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ================================
|
||||||
|
// Chat Schemas
|
||||||
|
// ================================
|
||||||
|
|
||||||
|
export const ChatModeEnum = z.enum(["stream", "job"]);
|
||||||
|
export const ChatRoleEnum = z.enum(["user", "assistant", "system"]);
|
||||||
|
export const ChatBlockTypeEnum = z.enum([
|
||||||
|
"text",
|
||||||
|
"mermaid",
|
||||||
|
"excalidraw_image",
|
||||||
|
"code",
|
||||||
|
"table",
|
||||||
|
]);
|
||||||
|
export const ChatEventTypeEnum = z.enum([
|
||||||
|
"start",
|
||||||
|
"progress",
|
||||||
|
"delta",
|
||||||
|
"artifact",
|
||||||
|
"final",
|
||||||
|
"error",
|
||||||
|
]);
|
||||||
|
export const ChatJobStatusEnum = z.enum(["queued", "running", "completed", "failed"]);
|
||||||
|
export const ChatFeedbackRatingEnum = z.enum(["helpful", "unhelpful"]);
|
||||||
|
|
||||||
|
export const ChatCapabilitySchema = z.object({
|
||||||
|
allowMermaid: z.boolean().optional().default(true),
|
||||||
|
allowExcalidrawImage: z.boolean().optional().default(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatCitationSchema = z.object({
|
||||||
|
title: z.string().min(1).max(200),
|
||||||
|
url: z.string().url().max(2000),
|
||||||
|
snippet: z.string().max(1000).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatMessageBlockSchema = z
|
||||||
|
.object({
|
||||||
|
type: ChatBlockTypeEnum,
|
||||||
|
title: z.string().max(200).optional(),
|
||||||
|
content: z.string().max(50000).optional(),
|
||||||
|
url: z.string().url().max(2000).optional(),
|
||||||
|
language: z.string().max(50).optional(),
|
||||||
|
rows: z.array(z.array(z.string().max(500))).optional(),
|
||||||
|
headers: z.array(z.string().max(200)).optional(),
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
if ((data.type === "text" || data.type === "mermaid" || data.type === "code") && !data.content) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
path: ["content"],
|
||||||
|
message: `content is required for block type: ${data.type}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.type === "excalidraw_image" && !data.url) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
path: ["url"],
|
||||||
|
message: "url is required for block type: excalidraw_image",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.type === "table" && (!data.headers || !data.rows)) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
path: ["rows"],
|
||||||
|
message: "headers and rows are required for block type: table",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatAssistantMessageSchema = z.object({
|
||||||
|
id: z.string().min(1).max(100).optional(),
|
||||||
|
role: z.literal("assistant").default("assistant"),
|
||||||
|
blocks: z.array(ChatMessageBlockSchema).default([]),
|
||||||
|
citations: z.array(ChatCitationSchema).optional().default([]),
|
||||||
|
meta: z.record(z.unknown()).optional(),
|
||||||
|
rawN8nPayload: z.unknown().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatMessageRequestSchema = z.object({
|
||||||
|
sessionId: z.string().min(1).max(100).optional(),
|
||||||
|
clientId: z.string().min(8).max(100),
|
||||||
|
locale: z.enum(["zh", "en"]),
|
||||||
|
mode: ChatModeEnum.default("job"),
|
||||||
|
message: z.string().min(1).max(8000),
|
||||||
|
capabilities: ChatCapabilitySchema.optional().default({}),
|
||||||
|
context: JsonObjectSchema.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatSessionQuerySchema = z.object({
|
||||||
|
clientId: z.string().min(8).max(100),
|
||||||
|
locale: z.enum(["zh", "en"]).optional(),
|
||||||
|
limit: z.coerce.number().int().positive().max(50).default(20),
|
||||||
|
cursor: z.string().min(1).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatJobStatusQuerySchema = z.object({
|
||||||
|
clientId: z.string().min(8).max(100),
|
||||||
|
sessionId: z.string().min(1).max(100),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatFeedbackRequestSchema = z.object({
|
||||||
|
clientId: z.string().min(8).max(100),
|
||||||
|
sessionId: z.string().min(1).max(100),
|
||||||
|
messageId: z.string().min(1).max(100),
|
||||||
|
rating: ChatFeedbackRatingEnum,
|
||||||
|
reason: z.string().max(500).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const N8NChatJobResponseSchema = z.object({
|
||||||
|
requestId: z.string().min(1).optional(),
|
||||||
|
jobId: z.string().min(1),
|
||||||
|
status: ChatJobStatusEnum,
|
||||||
|
progress: z
|
||||||
|
.object({
|
||||||
|
stage: z.string().max(100).optional(),
|
||||||
|
message: z.string().max(500).optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
message: ChatAssistantMessageSchema.optional(),
|
||||||
|
error: z
|
||||||
|
.object({
|
||||||
|
code: z.string().max(100).optional(),
|
||||||
|
message: z.string().max(500),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
meta: z.record(z.unknown()).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
// Types
|
// Types
|
||||||
// ================================
|
// ================================
|
||||||
@@ -175,6 +308,20 @@ export type GetDiscoveryTasksQuery = z.infer<typeof GetDiscoveryTasksQuerySchema
|
|||||||
export type BatchResetTasks = z.infer<typeof BatchResetTasksSchema>;
|
export type BatchResetTasks = z.infer<typeof BatchResetTasksSchema>;
|
||||||
export type AIEventInput = z.infer<typeof AIEventInputSchema>;
|
export type AIEventInput = z.infer<typeof AIEventInputSchema>;
|
||||||
export type JsonObject = z.infer<typeof JsonObjectSchema>;
|
export type JsonObject = z.infer<typeof JsonObjectSchema>;
|
||||||
|
export type ChatMode = z.infer<typeof ChatModeEnum>;
|
||||||
|
export type ChatRole = z.infer<typeof ChatRoleEnum>;
|
||||||
|
export type ChatBlockType = z.infer<typeof ChatBlockTypeEnum>;
|
||||||
|
export type ChatEventType = z.infer<typeof ChatEventTypeEnum>;
|
||||||
|
export type ChatJobStatus = z.infer<typeof ChatJobStatusEnum>;
|
||||||
|
export type ChatFeedbackRating = z.infer<typeof ChatFeedbackRatingEnum>;
|
||||||
|
export type ChatCitation = z.infer<typeof ChatCitationSchema>;
|
||||||
|
export type ChatMessageBlock = z.infer<typeof ChatMessageBlockSchema>;
|
||||||
|
export type ChatAssistantMessage = z.infer<typeof ChatAssistantMessageSchema>;
|
||||||
|
export type ChatMessageRequest = z.infer<typeof ChatMessageRequestSchema>;
|
||||||
|
export type ChatSessionQuery = z.infer<typeof ChatSessionQuerySchema>;
|
||||||
|
export type ChatJobStatusQuery = z.infer<typeof ChatJobStatusQuerySchema>;
|
||||||
|
export type ChatFeedbackRequest = z.infer<typeof ChatFeedbackRequestSchema>;
|
||||||
|
export type N8NChatJobResponse = z.infer<typeof N8NChatJobResponseSchema>;
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
// Tags API Schemas
|
// Tags API Schemas
|
||||||
|
|||||||
Reference in New Issue
Block a user