From 956a37d825bd7cc6f551e43530f70197c29935b7 Mon Sep 17 00:00:00 2001 From: fly6516 Date: Tue, 17 Jun 2025 01:35:04 +0800 Subject: [PATCH] feat(problem-editor): realize backend for save problem edit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在编辑面板组件中添加 onUpdate属性,用于处理数据更新 - 新增 updateProblem 函数,实现问题数据的更新逻辑 - 在问题编辑页面中集成更新功能,实现即时保存 --- .../(app)/problem-editor/[problemId]/page.tsx | 111 +++++++++++++-- src/app/actions/updateProblem.ts | 127 ++++++++++++++++++ src/components/creater/edit-code-panel.tsx | 4 + .../creater/edit-description-panel.tsx | 3 +- src/components/creater/edit-detail-panel.tsx | 1 + .../creater/edit-solution-panel.tsx | 3 +- .../creater/edit-testcase-panel.tsx | 4 + 7 files changed, 240 insertions(+), 13 deletions(-) create mode 100644 src/app/actions/updateProblem.ts diff --git a/src/app/(app)/problem-editor/[problemId]/page.tsx b/src/app/(app)/problem-editor/[problemId]/page.tsx index 44bb27f..6047c80 100644 --- a/src/app/(app)/problem-editor/[problemId]/page.tsx +++ b/src/app/(app)/problem-editor/[problemId]/page.tsx @@ -1,27 +1,116 @@ "use client"; -import { ProblemFlexLayout } from "@/features/problems/components/problem-flexlayout"; -import { EditDescriptionPanel } from "@/components/creater/edit-description-panel"; -import { EditSolutionPanel } from "@/components/creater/edit-solution-panel"; -import { EditTestcasePanel } from "@/components/creater/edit-testcase-panel"; -import { EditDetailPanel } from "@/components/creater/edit-detail-panel"; -import { EditCodePanel } from "@/components/creater/edit-code-panel"; +import { ProblemFlexLayout } from '@/features/problems/components/problem-flexlayout'; +import { EditDescriptionPanel } from '@/components/creater/edit-description-panel'; +import { EditSolutionPanel } from '@/components/creater/edit-solution-panel'; +import { EditTestcasePanel } from '@/components/creater/edit-testcase-panel'; +import { EditDetailPanel } from '@/components/creater/edit-detail-panel'; +import { EditCodePanel } from '@/components/creater/edit-code-panel'; +import { updateProblem } from '@/app/actions/updateProblem'; interface ProblemEditorPageProps { params: Promise<{ problemId: string }>; } +interface UpdateData { + content: string; + language?: 'c' | 'cpp'; + inputs?: Array<{ index: number; name: string; value: string }>; +} + +const handleUpdate = async ( + updateFn: (data: UpdateData) => Promise<{ success: boolean }>, + data: UpdateData +) => { + try { + const result = await updateFn(data); + if (!result.success) { + // 这里可以添加更具体的错误处理 + } + return result; + } catch (error) { + console.error('更新失败:', error); + return { success: false }; + } +}; + export default async function ProblemEditorPage({ params, }: ProblemEditorPageProps) { const { problemId } = await params; const components: Record = { - description: , - solution: , - detail: , - code: , - testcase: , + description: { + await handleUpdate( + (descriptionData) => updateProblem({ + problemId, + displayId: 0, + description: descriptionData.content + }), + data + ); + }} + />, + solution: { + await handleUpdate( + (solutionData) => updateProblem({ + problemId, + displayId: 0, + solution: solutionData.content + }), + data + ); + }} + />, + detail: { + await handleUpdate( + (detailData) => updateProblem({ + problemId, + displayId: 0, + detail: detailData.content + }), + data + ); + }} + />, + code: { + await handleUpdate( + (codeData) => updateProblem({ + problemId, + displayId: 0, + templates: [{ + language: codeData.language || 'c', // 添加默认值 + content: codeData.content + }] + }), + data + ); + }} + />, + testcase: { + await handleUpdate( + (testcaseData) => updateProblem({ + problemId, + displayId: 0, + testcases: [{ + expectedOutput: testcaseData.content, + inputs: testcaseData.inputs || [] // 添加默认空数组 + }] + }), + data + ); + }} + /> }; return ( diff --git a/src/app/actions/updateProblem.ts b/src/app/actions/updateProblem.ts new file mode 100644 index 0000000..c06e67c --- /dev/null +++ b/src/app/actions/updateProblem.ts @@ -0,0 +1,127 @@ +'use server'; + +import prisma from '@/lib/prisma'; +import { revalidatePath } from 'next/cache'; +import { z } from 'zod'; + +const ProblemUpdateSchema = z.object({ + problemId: z.string(), + displayId: z.number().optional(), // 改回可选字段 + difficulty: z.enum(['EASY', 'MEDIUM', 'HARD']).optional(), + isPublished: z.boolean().optional(), + timeLimit: z.number().optional(), + memoryLimit: z.number().optional(), + description: z.string().optional(), + solution: z.string().optional(), + detail: z.string().optional(), + templates: z.array(z.object({ + language: z.enum(['c', 'cpp']), + content: z.string() + })).optional(), + testcases: z.array(z.object({ + expectedOutput: z.string(), + inputs: z.array(z.object({ + index: z.number(), + name: z.string(), + value: z.string() + })) + })).optional() +}); + +export type UpdateProblemData = z.infer; + +export async function updateProblem(data: z.infer) { + try { + const validatedData = ProblemUpdateSchema.parse(data); + + // 使用upsert代替update实现存在时更新,不存在时创建 + const problem = await prisma.problem.upsert({ + where: { id: validatedData.problemId }, + create: { + id: validatedData.problemId, // 需要显式指定ID + displayId: validatedData.displayId || 0, + difficulty: validatedData.difficulty || 'EASY', + isPublished: validatedData.isPublished || false, + timeLimit: validatedData.timeLimit || 1000, + memoryLimit: validatedData.memoryLimit || 134217728, + // 初始化关联数据 + localizations: validatedData.description ? { + create: [{ + locale: 'en', + type: 'DESCRIPTION', + content: validatedData.description + }] + } : undefined, + templates: validatedData.templates ? { + create: validatedData.templates.map(t => ({ + language: t.language, + content: t.content + })) + } : undefined, + testcases: validatedData.testcases ? { + create: validatedData.testcases.map(t => ({ + expectedOutput: t.expectedOutput, + inputs: { + create: t.inputs.map(i => ({ + index: i.index, + name: i.name, + value: i.value + })) + } + })) + } : undefined + }, + update: { + displayId: validatedData.displayId, + difficulty: validatedData.difficulty, + isPublished: validatedData.isPublished, + timeLimit: validatedData.timeLimit, + memoryLimit: validatedData.memoryLimit, + // 更新关联数据 + localizations: validatedData.description ? { + upsert: { + where: { problemId_locale_type: { + problemId: validatedData.problemId, + locale: 'en', + type: 'DESCRIPTION' + }}, + create: { + locale: 'en', + type: 'DESCRIPTION', + content: validatedData.description + }, + update: { + content: validatedData.description + } + } + } : undefined, + templates: validatedData.templates ? { + deleteMany: {}, + create: validatedData.templates.map(t => ({ + language: t.language, + content: t.content + })) + } : undefined, + testcases: validatedData.testcases ? { + deleteMany: {}, + create: validatedData.testcases.map(t => ({ + expectedOutput: t.expectedOutput, + inputs: { + create: t.inputs.map(i => ({ + index: i.index, + name: i.name, + value: i.value + })) + } + })) + } : undefined + } + }); + + revalidatePath(`/problem-editor/${validatedData.problemId}`); + return { success: true, problem }; + } catch (error) { + console.error('Failed to update problem:', error); + return { success: false, error: 'Failed to update problem' }; + } +} \ No newline at end of file diff --git a/src/components/creater/edit-code-panel.tsx b/src/components/creater/edit-code-panel.tsx index 5af31d6..faeacfe 100644 --- a/src/components/creater/edit-code-panel.tsx +++ b/src/components/creater/edit-code-panel.tsx @@ -14,6 +14,10 @@ interface Template { interface EditCodePanelProps { problemId: string; + onUpdate?: (data: { + content: string; + language: 'c' | 'cpp'; // 移除可选标记 + }) => void; } export const EditCodePanel = ({ diff --git a/src/components/creater/edit-description-panel.tsx b/src/components/creater/edit-description-panel.tsx index d34c781..9958a56 100644 --- a/src/components/creater/edit-description-panel.tsx +++ b/src/components/creater/edit-description-panel.tsx @@ -10,6 +10,7 @@ import {CoreEditor} from "@/components/core-editor"; interface EditDescriptionPanelProps { problemId: string; + onUpdate?: (data: { content: string }) => void; } export const EditDescriptionPanel = ({ @@ -65,7 +66,7 @@ export const EditDescriptionPanel = ({ diff --git a/src/components/creater/edit-detail-panel.tsx b/src/components/creater/edit-detail-panel.tsx index 387e7a6..f7963e3 100644 --- a/src/components/creater/edit-detail-panel.tsx +++ b/src/components/creater/edit-detail-panel.tsx @@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button"; interface EditDetailPanelProps { problemId: string; + onUpdate?: (data: { content: string }) => void; } export const EditDetailPanel = ({ diff --git a/src/components/creater/edit-solution-panel.tsx b/src/components/creater/edit-solution-panel.tsx index bf1eb03..1333e3d 100644 --- a/src/components/creater/edit-solution-panel.tsx +++ b/src/components/creater/edit-solution-panel.tsx @@ -10,6 +10,7 @@ import {CoreEditor} from "@/components/core-editor"; interface EditSolutionPanelProps { problemId: string; + onUpdate?: (data: { content: string }) => void; } export const EditSolutionPanel = ({ @@ -65,7 +66,7 @@ export const EditSolutionPanel = ({ diff --git a/src/components/creater/edit-testcase-panel.tsx b/src/components/creater/edit-testcase-panel.tsx index ac829e3..bda59cc 100644 --- a/src/components/creater/edit-testcase-panel.tsx +++ b/src/components/creater/edit-testcase-panel.tsx @@ -21,6 +21,10 @@ interface TestCase { interface EditTestcasePanelProps { problemId: string; + onUpdate?: (data: { + content: string; + inputs: Array<{ index: number; name: string; value: string }> + }) => void; } export const EditTestcasePanel = ({