/** * Prompt 管理 API 控制器 */ import type { FastifyRequest, FastifyReply } from 'fastify'; import { getPromptService } from './prompt.service.js'; import type { ModelConfig } from './prompt.types.js'; import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // 请求类型定义 interface ListPromptsQuery { module?: string; } interface GetPromptParams { code: string; } interface SaveDraftBody { content: string; modelConfig?: ModelConfig; changelog?: string; } interface PublishBody { // 可扩展 } interface RollbackBody { version: number; } interface SetDebugModeBody { modules: string[]; enabled: boolean; } interface TestRenderBody { content: string; variables: Record; } /** * 获取 Prompt 列表 * GET /api/admin/prompts?module=RVW */ export async function listPrompts( request: FastifyRequest<{ Querystring: ListPromptsQuery }>, reply: FastifyReply ) { try { const { module } = request.query; const promptService = getPromptService(prisma); const templates = await promptService.listTemplates(module); // 转换为 API 响应格式,分别返回 ACTIVE 和 DRAFT 版本 const result = templates.map(t => { const activeVersion = t.versions.find(v => v.status === 'ACTIVE'); const draftVersion = t.versions.find(v => v.status === 'DRAFT'); // 调试日志 console.log(`[PromptController] ${t.code} 版本数量: ${t.versions.length}`); console.log(` - ACTIVE: ${activeVersion ? 'v' + activeVersion.version : '无'}`); console.log(` - DRAFT: ${draftVersion ? 'v' + draftVersion.version : '无'}`); return { id: t.id, code: t.code, name: t.name, module: t.module, description: t.description, variables: t.variables, activeVersion: activeVersion ? { version: activeVersion.version, status: activeVersion.status, createdAt: activeVersion.created_at, } : null, draftVersion: draftVersion ? { version: draftVersion.version, status: draftVersion.status, createdAt: draftVersion.created_at, } : null, latestVersion: t.versions[0] ? { version: t.versions[0].version, status: t.versions[0].status, createdAt: t.versions[0].created_at, } : null, updatedAt: t.updated_at, }; }); console.log('[PromptController] 返回数据示例:', JSON.stringify(result[0], null, 2)); return reply.send({ success: true, data: result, total: result.length, }); } catch (error) { console.error('[PromptController] listPrompts error:', error); return reply.status(500).send({ success: false, error: 'Failed to list prompts', }); } } /** * 获取 Prompt 详情(含版本历史) * GET /api/admin/prompts/:code */ export async function getPromptDetail( request: FastifyRequest<{ Params: GetPromptParams }>, reply: FastifyReply ) { try { const { code } = request.params; const promptService = getPromptService(prisma); const template = await promptService.getTemplateDetail(code); if (!template) { return reply.status(404).send({ success: false, error: 'Prompt not found', }); } // 转换版本历史 const versions = template.versions.map(v => ({ id: v.id, version: v.version, status: v.status, content: v.content, modelConfig: v.model_config, changelog: v.changelog, createdBy: v.created_by, createdAt: v.created_at, })); return reply.send({ success: true, data: { id: template.id, code: template.code, name: template.name, module: template.module, description: template.description, variables: template.variables, versions, createdAt: template.created_at, updatedAt: template.updated_at, }, }); } catch (error) { console.error('[PromptController] getPromptDetail error:', error); return reply.status(500).send({ success: false, error: 'Failed to get prompt detail', }); } } /** * 保存草稿 * POST /api/admin/prompts/:code/draft * * 需要权限: prompt:edit */ export async function saveDraft( request: FastifyRequest<{ Params: GetPromptParams; Body: SaveDraftBody }>, reply: FastifyReply ) { try { const { code } = request.params; const { content, modelConfig, changelog } = request.body; const userId = (request as any).user?.userId; const promptService = getPromptService(prisma); // 保存草稿 const draft = await promptService.saveDraft( code, content, modelConfig, changelog, userId ); // 提取变量信息 const variables = promptService.extractVariables(content); return reply.send({ success: true, data: { id: draft.id, version: draft.version, status: draft.status, variables, message: 'Draft saved successfully', }, }); } catch (error: any) { console.error('[PromptController] saveDraft error:', error); return reply.status(400).send({ success: false, error: error.message || 'Failed to save draft', }); } } /** * 发布 Prompt * POST /api/admin/prompts/:code/publish * * 需要权限: prompt:publish */ export async function publishPrompt( request: FastifyRequest<{ Params: GetPromptParams; Body: PublishBody }>, reply: FastifyReply ) { try { const { code } = request.params; const userId = (request as any).user?.userId; const promptService = getPromptService(prisma); const published = await promptService.publish(code, userId); return reply.send({ success: true, data: { version: published.version, status: 'ACTIVE', message: `Prompt ${code} published successfully (v${published.version})`, }, }); } catch (error: any) { console.error('[PromptController] publishPrompt error:', error); return reply.status(400).send({ success: false, error: error.message || 'Failed to publish prompt', }); } } /** * 回滚到指定版本 * POST /api/admin/prompts/:code/rollback * * 需要权限: prompt:publish */ export async function rollbackPrompt( request: FastifyRequest<{ Params: GetPromptParams; Body: RollbackBody }>, reply: FastifyReply ) { try { const { code } = request.params; const { version } = request.body; const userId = (request as any).user?.userId; const promptService = getPromptService(prisma); const rolled = await promptService.rollback(code, version, userId); return reply.send({ success: true, data: { version: rolled.version, status: 'ACTIVE', message: `Prompt ${code} rolled back to v${version}`, }, }); } catch (error: any) { console.error('[PromptController] rollbackPrompt error:', error); return reply.status(400).send({ success: false, error: error.message || 'Failed to rollback prompt', }); } } /** * 设置调试模式 * POST /api/admin/prompts/debug * * 需要权限: prompt:debug */ export async function setDebugMode( request: FastifyRequest<{ Body: SetDebugModeBody }>, reply: FastifyReply ) { try { const { modules, enabled } = request.body; const userId = (request as any).user?.userId; if (!userId) { return reply.status(401).send({ success: false, error: 'User not authenticated', }); } const promptService = getPromptService(prisma); promptService.setDebugMode(userId, modules, enabled); const state = promptService.getDebugState(userId); return reply.send({ success: true, data: { userId, modules: state ? Array.from(state.modules) : [], enabled, message: enabled ? `Debug mode enabled for modules: [${modules.join(', ')}]` : 'Debug mode disabled', }, }); } catch (error: any) { console.error('[PromptController] setDebugMode error:', error); return reply.status(500).send({ success: false, error: error.message || 'Failed to set debug mode', }); } } /** * 获取当前用户的调试状态 * GET /api/admin/prompts/debug */ export async function getDebugStatus( request: FastifyRequest, reply: FastifyReply ) { try { const userId = (request as any).user?.userId; if (!userId) { return reply.status(401).send({ success: false, error: 'User not authenticated', }); } const promptService = getPromptService(prisma); const state = promptService.getDebugState(userId); return reply.send({ success: true, data: { userId, isDebugging: !!state, modules: state ? Array.from(state.modules) : [], enabledAt: state?.enabledAt, }, }); } catch (error: any) { console.error('[PromptController] getDebugStatus error:', error); return reply.status(500).send({ success: false, error: error.message || 'Failed to get debug status', }); } } /** * 测试渲染 Prompt * POST /api/admin/prompts/test-render */ export async function testRender( request: FastifyRequest<{ Body: TestRenderBody }>, reply: FastifyReply ) { try { const { content, variables } = request.body; const promptService = getPromptService(prisma); // 提取变量 const extractedVars = promptService.extractVariables(content); // 校验变量 const validation = promptService.validateVariables(content, variables); // 渲染 const rendered = promptService.render(content, variables); return reply.send({ success: true, data: { rendered, extractedVariables: extractedVars, validation, }, }); } catch (error: any) { console.error('[PromptController] testRender error:', error); return reply.status(400).send({ success: false, error: error.message || 'Failed to render prompt', }); } } /** * 清除缓存 * POST /api/admin/prompts/:code/invalidate-cache */ export async function invalidateCache( request: FastifyRequest<{ Params: GetPromptParams }>, reply: FastifyReply ) { try { const { code } = request.params; const promptService = getPromptService(prisma); promptService.invalidateCache(code); return reply.send({ success: true, data: { code, message: `Cache invalidated for ${code}`, }, }); } catch (error: any) { console.error('[PromptController] invalidateCache error:', error); return reply.status(500).send({ success: false, error: error.message || 'Failed to invalidate cache', }); } }