Summary: - Implement Prompt management infrastructure and core services - Build admin portal frontend with light theme - Integrate CodeMirror 6 editor for non-technical users Phase 3.5.1: Infrastructure Setup - Create capability_schema for Prompt storage - Add prompt_templates and prompt_versions tables - Add prompt:view/edit/debug/publish permissions - Migrate RVW prompts to database (RVW_EDITORIAL, RVW_METHODOLOGY) Phase 3.5.2: PromptService Core - Implement gray preview logic (DRAFT for debuggers, ACTIVE for users) - Module-level debug control (setDebugMode) - Handlebars template rendering - Variable extraction and validation (extractVariables, validateVariables) - Three-level disaster recovery (database -> cache -> hardcoded fallback) Phase 3.5.3: Management API - 8 RESTful endpoints (/api/admin/prompts/*) - Permission control (PROMPT_ENGINEER can edit, SUPER_ADMIN can publish) Phase 3.5.4: Frontend Management UI - Build admin portal architecture (AdminLayout, OrgLayout) - Add route system (/admin/*, /org/*) - Implement PromptListPage (filter, search, debug switch) - Implement PromptEditor (CodeMirror 6 simplified for clinical users) - Implement PromptEditorPage (edit, save, publish, test, version history) Technical Details: - Backend: 6 files, ~2044 lines (prompt.service.ts 596 lines) - Frontend: 9 files, ~1735 lines (PromptEditorPage.tsx 399 lines) - CodeMirror 6: Line numbers, auto-wrap, variable highlight, search, undo/redo - Chinese-friendly: 15px font, 1.8 line-height, system fonts Next Step: Phase 3.5.5 - Integrate RVW module with PromptService Tested: Backend API tests passed (8/8), Frontend pending user testing Status: Ready for Phase 3.5.5 RVW integration
161 lines
5.0 KiB
TypeScript
161 lines
5.0 KiB
TypeScript
/**
|
||
* RVW模块 Prompt 迁移脚本
|
||
*
|
||
* 将现有文件Prompt迁移到数据库
|
||
*
|
||
* 迁移内容:
|
||
* 1. RVW_EDITORIAL - 稿约规范性评估 (review_editorial_system.txt)
|
||
* 2. RVW_METHODOLOGY - 方法学质量评估 (review_methodology_system.txt)
|
||
* 3. RVW_TOPIC_SYSTEM - 选题评估系统提示 (topic_evaluation_system.txt)
|
||
* 4. RVW_TOPIC_USER - 选题评估用户模板 (topic_evaluation_user.txt)
|
||
*/
|
||
|
||
import { PrismaClient, PromptStatus } from '@prisma/client';
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = path.dirname(__filename);
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
// 变量提取函数
|
||
function extractVariables(content: string): string[] {
|
||
const regex = /\{\{(\w+)\}\}/g;
|
||
const variables = new Set<string>();
|
||
let match;
|
||
while ((match = regex.exec(content)) !== null) {
|
||
// 排除 Handlebars 控制语句如 #if, /if
|
||
if (!match[1].startsWith('#') && !match[1].startsWith('/')) {
|
||
variables.add(match[1]);
|
||
}
|
||
}
|
||
return Array.from(variables);
|
||
}
|
||
|
||
// RVW Prompt 配置(只有 2 个)
|
||
// 注意:topic_evaluation_* 是"选题评估"功能,不属于 RVW 审稿模块
|
||
const rvwPrompts = [
|
||
{
|
||
code: 'RVW_EDITORIAL',
|
||
name: '稿约规范性评估',
|
||
module: 'RVW',
|
||
description: '评估医学稿件是否符合期刊稿约规范,包括文题、摘要、参考文献等11项标准。输出JSON格式的评分和建议。',
|
||
file: 'review_editorial_system.txt',
|
||
modelConfig: { model: 'deepseek-v3', temperature: 0.3 },
|
||
},
|
||
{
|
||
code: 'RVW_METHODOLOGY',
|
||
name: '方法学质量评估',
|
||
module: 'RVW',
|
||
description: '评估医学稿件的科研设计、统计学方法和统计分析质量,共20个检查点。输出JSON格式的评分和建议。',
|
||
file: 'review_methodology_system.txt',
|
||
modelConfig: { model: 'deepseek-v3', temperature: 0.3 },
|
||
},
|
||
];
|
||
|
||
async function main() {
|
||
console.log('🚀 开始迁移 RVW Prompt 到数据库...\n');
|
||
|
||
const promptsDir = path.join(__dirname, '..', 'prompts');
|
||
|
||
for (const prompt of rvwPrompts) {
|
||
console.log(`📄 处理: ${prompt.code} (${prompt.name})`);
|
||
|
||
// 读取文件内容
|
||
const filePath = path.join(promptsDir, prompt.file);
|
||
if (!fs.existsSync(filePath)) {
|
||
console.log(` ⚠️ 文件不存在: ${filePath}`);
|
||
continue;
|
||
}
|
||
|
||
const content = fs.readFileSync(filePath, 'utf-8').trim();
|
||
const variables = extractVariables(content);
|
||
|
||
console.log(` 📝 内容长度: ${content.length} 字符`);
|
||
console.log(` 🔤 提取变量: [${variables.join(', ')}]`);
|
||
|
||
// 创建或更新模板
|
||
const template = await prisma.prompt_templates.upsert({
|
||
where: { code: prompt.code },
|
||
update: {
|
||
name: prompt.name,
|
||
description: prompt.description,
|
||
variables: variables.length > 0 ? variables : null,
|
||
updated_at: new Date(),
|
||
},
|
||
create: {
|
||
code: prompt.code,
|
||
name: prompt.name,
|
||
module: prompt.module,
|
||
description: prompt.description,
|
||
variables: variables.length > 0 ? variables : null,
|
||
},
|
||
});
|
||
|
||
// 检查是否已有 ACTIVE 版本
|
||
const existingActive = await prisma.prompt_versions.findFirst({
|
||
where: {
|
||
template_id: template.id,
|
||
status: PromptStatus.ACTIVE,
|
||
},
|
||
});
|
||
|
||
if (existingActive) {
|
||
console.log(` ✅ 已存在 ACTIVE 版本 (v${existingActive.version})`);
|
||
} else {
|
||
// 创建第一个 ACTIVE 版本
|
||
await prisma.prompt_versions.create({
|
||
data: {
|
||
template_id: template.id,
|
||
version: 1,
|
||
content: content,
|
||
model_config: prompt.modelConfig,
|
||
status: PromptStatus.ACTIVE,
|
||
changelog: '从文件迁移的初始版本',
|
||
created_by: 'system-migration',
|
||
},
|
||
});
|
||
console.log(` ✅ 创建 ACTIVE 版本 (v1)`);
|
||
}
|
||
|
||
console.log('');
|
||
}
|
||
|
||
// 验证结果
|
||
console.log('═══════════════════════════════════════════════════════');
|
||
console.log('📊 迁移结果验证\n');
|
||
|
||
const templates = await prisma.prompt_templates.findMany({
|
||
where: { module: 'RVW' },
|
||
include: {
|
||
versions: {
|
||
orderBy: { version: 'desc' },
|
||
take: 1,
|
||
},
|
||
},
|
||
});
|
||
|
||
console.log(`✅ 共迁移 ${templates.length} 个 RVW Prompt:\n`);
|
||
|
||
for (const t of templates) {
|
||
const latestVersion = t.versions[0];
|
||
console.log(` 📋 ${t.code}`);
|
||
console.log(` 名称: ${t.name}`);
|
||
console.log(` 变量: ${t.variables ? JSON.stringify(t.variables) : '无'}`);
|
||
console.log(` 最新版本: v${latestVersion?.version} (${latestVersion?.status})`);
|
||
console.log('');
|
||
}
|
||
|
||
console.log('✅ RVW Prompt 迁移完成!');
|
||
}
|
||
|
||
main()
|
||
.catch((error) => {
|
||
console.error('❌ 迁移失败:', error);
|
||
process.exit(1);
|
||
})
|
||
.finally(() => prisma.$disconnect());
|
||
|