From e745b7339b09bcca87b60a1321830b2f80099746 Mon Sep 17 00:00:00 2001 From: ddshi <8811906+ddshi@user.noreply.gitee.com> Date: Thu, 29 Jan 2026 16:59:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0AI=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E6=95=B0=E6=8D=AEZod=20Schema=E9=AA=8C=E8=AF=81=20(P0=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E4=BF=AE=E5=A4=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/ai.ts | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/routes/ai.ts b/src/routes/ai.ts index bdcbd04..eb77571 100644 --- a/src/routes/ai.ts +++ b/src/routes/ai.ts @@ -1,6 +1,6 @@ import { Router, Request, Response } from 'express'; import { z } from 'zod'; -import prisma from '../lib/prisma'; +import db from '../lib/db'; import { authenticateToken, AuthenticatedRequest } from '../middleware/auth'; import { asyncHandler } from '../middleware/errorHandler'; @@ -10,6 +10,15 @@ const parseMessageSchema = z.object({ message: z.string().min(1, 'Message is required').max(1000), }); +// AI parsed event validation schema +const parsedEventSchema = z.object({ + type: z.enum(['anniversary', 'reminder']), + title: z.string().min(1).max(200), + date: z.string().datetime(), + is_lunar: z.boolean(), + repeat_type: z.enum(['yearly', 'monthly', 'none']), +}); + // All routes require authentication router.use(authenticateToken); @@ -23,19 +32,17 @@ router.post( const aiResponse = await callDeepSeek(message); // Save conversation - const conversation = await prisma.aICoachConversation.create({ - data: { - user_id: req.user!.userId, - message, - response: aiResponse.response, - parsed_data: aiResponse.parsed, - }, + const convId = crypto.randomUUID(); + await db.execute({ + sql: `INSERT INTO ai_coach_conversations (id, user_id, message, response, parsed_data, created_at) + VALUES (?, ?, ?, ?, ?, datetime('now'))`, + args: [convId, req.user!.userId, message, aiResponse.response, JSON.stringify(aiResponse.parsed)], }); res.json({ parsed: aiResponse.parsed, response: aiResponse.response, - conversation_id: conversation.id, + conversation_id: convId, }); }) ); @@ -97,11 +104,19 @@ async function callDeepSeek(message: string): Promise<{ // Extract JSON from response const jsonMatch = content.match(/\{[\s\S]*\}/); if (jsonMatch) { - const parsed = JSON.parse(jsonMatch[0]); - return { - parsed: parsed.parsed || parsed, - response: parsed.response || content, - }; + try { + const rawParsed = JSON.parse(jsonMatch[0]); + const parsed = rawParsed.parsed || rawParsed; + // Validate parsed data with Zod schema + const validated = parsedEventSchema.parse(parsed); + return { + parsed: validated, + response: parsed.response || content, + }; + } catch (e) { + // Schema validation failed, use mock response + return mockParseResponse(message); + } } return mockParseResponse(message);