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({
|
||||
status: searchParams.get('status') || undefined,
|
||||
statuses: searchParams.get('statuses') || undefined, // 新增:支持多状态查询
|
||||
limit: searchParams.get('limit') || '10',
|
||||
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({
|
||||
where: status ? { status } : undefined,
|
||||
where: whereClause,
|
||||
orderBy: { createdAt: 'asc' },
|
||||
take: limit,
|
||||
skip: offset,
|
||||
})
|
||||
|
||||
const total = await prisma.projectDiscoveryTask.count({
|
||||
where: status ? { status } : undefined,
|
||||
where: whereClause,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
|
||||
+148
-1
@@ -104,7 +104,12 @@ export const UpdateDiscoveryTaskSchema = 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),
|
||||
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),
|
||||
});
|
||||
|
||||
// ================================
|
||||
// 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
|
||||
// ================================
|
||||
@@ -175,6 +308,20 @@ export type GetDiscoveryTasksQuery = z.infer<typeof GetDiscoveryTasksQuerySchema
|
||||
export type BatchResetTasks = z.infer<typeof BatchResetTasksSchema>;
|
||||
export type AIEventInput = z.infer<typeof AIEventInputSchema>;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user